📝 TSD3060 Eksamen 2022-2023

Ordinær eksamen - C-webtjener/HTML/JS/Sikkerhet

ℹ️ Om denne eksamenen

Type: Blandet kodeanalyse - C-programmering, chroot, namespaces, HTML/JS, cookies, serviceworkers

Fokus: Sockets, sikkerhetsteknologi, web-autentisering

Metode: Skriv egne svar først, sammenlign deretter med sensorveiledning

Oppgave 1 - C Web-tjener 30%

Del 1 (10%): Gi en overordnet beskrivelse (to–tre setninger) av hva programmet gjør.

Del 2 (10%): Gi en detaljert forklaring av koden f.o.m. linje 13. t.o.m. linje 19.

Del 3 (10%): Gi en detaljert forklaring av koden f.o.m. linje 21. t.o.m. linje 39.

1: #include <arpa/inet.h>
2: #include <unistd.h> 
3: #include <stdlib.h>
4: #include <stdio.h>
5: #define P 80
6: #define L 128 
7: 
8: int main ()
9: {
10:   struct sockaddr_in adr;
11:   int  s1, s2; ant; char buf[BUFSIZ];
12: 
13:   s1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
14:   adr.sin_family      = AF_INET;
15:   adr.sin_port        = htons((u_short)P); 
16:   adr.sin_addr.s_addr = htonl(INADDR_ANY);
17: 
18:   bind(s1, (struct sockaddr *)&adr, sizeof(adr));
19:   listen(s1, L); 
20: 
21:   while(1){ 
22: 
23:     s2 = accept(s1, NULL, NULL);    
24:     if(0==fork()) {
25: 
26:       dup2(s2, 1);
27:       printf("HTTP/1.1 200 OK\n"
28:              "Content-Type: text/plain\n"
29:              "\n"
30:              );
31:       fflush(stdout); 
32:       ant=read(s2,buf,BUFSIZ);
33:       write(s2, buf, ant);
34:       shutdown(s2, SHUT_RDWR);
35:       exit(0);
36:     }
37: 
38:     else { close(s2); }
39:   }
40: 
41:   return 0;

✅ Del 1 - Overordnet beskrivelse

Dette er en web-tjener som mottar HTTP-forespørsler og returnerer dem som et ekko. Hele HTTP-forespørselen (inkludert både hode og kropp) sendes i HTTP-responsens kropp.

✅ Del 2 - Linje 13-19

/* Linje 13: Oppretter TCP-socket for IPv4. 
   Returverdi er fildeskriptor. */
s1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

/* Linje 14-16: Initierer en adressestruktur slik at:
   - Socketen skal være en IPv4-socket (AF_INET)
   - Knyttes til lokal port nummer 80 (standard for HTTP)
   - Knyttes til alle vertens ip-adresser (INADDR_ANY = 0.0.0.0) */

adr.sin_family      = AF_INET;
adr.sin_port        = htons((u_short)P); 
adr.sin_addr.s_addr = htonl(INADDR_ANY);

/* Linje 18: Knyttes adresse-strukturen sammen med socketen */ 
bind(s1, (struct sockaddr *)&adr, sizeof(adr));

/* Linje 19: Setter socket s1 som passiv -- beregnet for mottak 
   av innkommende forespørsler. Maksimal kølengde for innkommende 
   forespørsler settes til L (128) */
listen(s1, L);

✅ Del 3 - Linje 21-39

/* Linje 21: En evig løkke, for kontinuerlig betjening av klientene */
while(1){ 

  /* Linje 23: Henter innkommende forespørsel fra kø av innkommende 
     forespørsler mot s1, setter opp en ny socket-struktur koblet til 
     forespørselens avsender og returnerer en referanse til denne 
     strukturen. Dersom køen er tom vil prosessen blokkeres inntil 
     forespørsel finnes i køen. */
  s2 = accept(s1, NULL, NULL);

  /* Linje 24: En ny prosess opprettes. Den nye prosessen kjører 
     koden i kodeblokken under */
  if(0==fork()) {

    /* Linje 26: Kopierer den nye socketens fildeskriptor til posisjon 
       1 i prosessens fildeskriptortabell, slik at standard utgang 
       (STDOUT) kobles til klienten. */
    dup2(s2, 1);

    /* Linje 27-30: Begynnelsen på en HTTP-respons skrives til STDOUT 
       (og dermed klienten). Hodet og en tom linje (som angir skillet 
       mellom HTTP-hodet og HTTP-kroppen) */
    printf("HTTP/1.1 200 OK\n"
           "Content-Type: text/plain\n"
           "\n");

    /* Linje 31: Minnelageret (bufferet) som printf-utskriften 
       midlertidig blir lagret i, tømmes. For å sikre at utskriften 
       av hodet blir sendt før kroppen. */
    fflush(stdout);

    /* Linje 32: Data leses maksimalt et antall byte fra klienten via 
       den nye socketen. BUFSIZ er et systemoptimalisert heltall 
       beregnet for i/o-operasjoner. */
    ant=read(s2,buf,BUFSIZ);

    /* Linje 33: Det som ble lest fra klienten, skrives nå til STDOUT, 
       slik at det sendes til klienten. Det vil utgjøre kroppen av 
       HTTP-responsen. */
    write(s2, buf, ant);

    /* Linje 34: Socketen stenges for både lesing og skriving */
    shutdown(s2, SHUT_RDWR);

    /* Linje 35: Prosessen, som ble opprettet for å kommunisere med 
       klienten, avsluttes. Tallet null indikerer normal avslutning 
       uten feil. */
    exit(0);
  }

  /* Linje 38: Den opprinnelige tjener-prosessen skal ikke kommunisere 
     med klienten, og lukker derfor socketen som er forbundet med 
     klienten. Deretter startes umiddelbart en ny runde i den evige 
     tjener-løkka. */
  else { close(s2); }
}
Oppgave 2 - chroot Sikkerhet 10%

Det er mulig å øke sikkerheten ved hjelp av chroot.

  • Forklar hva chroot er/gjør.
  • Hvordan kan chroot bidra til økt sikkerhet i tjener-programmer?
  • Forklar/vis hvordan sikkerheten kan bedres ved hjelp av chroot i koden som hører til denne oppgaven.

✅ Sensorveiledning

Hva er chroot?

chroot() er et systemkall som endrer den kallende prosessens rot-katalog.

Hvordan bidrar det til sikkerhet?

Ved å sette en ny rot, begrenses prosessens tilgang til filer. Dette vil kunne bidra til å hindre uautorisert tilgang til filer – som vil kunne gjøre systemet mer robust (tilgjengelighets-fremmende) og hindre data-lekasjer (konfidensialitet-fremmende).

Implementering i koden:

C-koden for å sette ny rotkatalog til f.eks. /var/www, er:

chroot("/var/www");

Jo tidligere dette gjøres, desto bedre/sikrere er det. Siden koden ikke åpner noen filer, er det ingen grunn til å vente med å gjøre dette. Det kan derfor gjøres helt i starten (rett etter linje 11).

Oppgave 3 - Linux Namespaces 10%

Forklar/vis og begrunn hvordan kjøring av koden, kan få økt sikkerhet ved hjelp av Linux-navnerom (Linux namespaces).

✅ Sensorveiledning

Prinsipp:

Ved å kjøre prosessen i avgrensede navnerom, vil en begrense prosessens tilgang til å gjelde et bestemt utvalg av systemets ressurser. Dette bidra til å hindre uautorisert tilgang, og gjør dermed systemet mer robust mot forstyrrelser, lekasjer og manipulasjon.

Typer navnerom (fra manual namespaces(7)):

Namespace   Constant          Isolates
Cgroup      CLONE_NEWCGROUP   Cgroup root directory
IPC         CLONE_NEWIPC      System V IPC, POSIX message queues
Network     CLONE_NEWNET      Network devices, stacks, ports, etc.
Mount       CLONE_NEWNS       Mount points
PID         CLONE_NEWPID      Process IDs
User        CLONE_NEWUSER     User and group IDs
UTS         CLONE_NEWUTS      Hostname and NIS domain name

Implementering:

Vi har i emnet sett på hvordan vi kan bruke programmene unshare og docker, for å kjøre kode i avgrensede navnerom.

Eksempel på kommando med unshare:

sudo unshare --user --map-root-user --fork --pid \\
     --mount --cgroup --ipc --uts --net ./tjener

Eksempel med Docker:

Når en Docker-container startes med f.eks. docker run en_eller_annen_konteiner, vil prosessene den består av kjøres i samme navnerom, slik at de alle får samme begrensninger/tilganger.

Oppgave 4 - HTML/JavaScript System 30%

Del 1 (05%): Gi en kortfattet og overordnet beskrivelse av hva dette er.

Del 2 (10%): Gi en detaljert forklaring av xyz.html.

Del 3 (10%): Gi en detaljert forklaring av xyz.js.

Del 4 (05%): Koden inneholder to trykk-knapper. Bruken av dem gir ulike resultat. Beskriv forskjellen.

Kode: xyz.html og xyz.js


1: <!doctype html>
2: <html>
3:   <head>
4:     <meta charset="utf-8">
5:     <title>XYZ</title>
6:     <link rel="stylesheet" type="text/css" href="xyz.css" />
7:     <script src="xyz.js"> </script>
8:   </head>
9:   <body>
10:     <form action='xyz.cgi' method='post'>
11:       <input name='X' id='x' type='text'> 
12:       <input type='submit'>
13:     </form>
14:     <button type="button" onclick='xyz()'> fetch </button>
15:     <div id='r'></div>
16:   </body>
17: </html>


function xyz(){
  let b = 'https://usn.no/';
  let u = new URL('xyz.cgi', b);                
  let t = document.querySelector('#x').value;
  let r = document.querySelector('#r');

  fetch( u, { method: 'post', body: t } )
    .then( respons => respons.text() )
    .then( k => r.innerHTML += k )
}

✅ Del 1 - Overordnet beskrivelse (5%)

Dette er kode som skal tolkes og kjøres i en nettleser (web browser). Det gir brukeren et felt for innskriving av tekst (tekstfelt) og to trykk-knapper.

Et trykk/klikk på en hvilken som helst av de to knappene utløser innsending av den innskrevne teksten til et CGI-skript. Det mottagende skriptet får den innsendte teksten på standard inngang (STDIN). Etter en evt. behandling av innlest tekst, sender skriptet tilbake til klienten. Hva som skjer i nettleseren ved mottak, avhenger av hvilken av knappene brukeren trykket på.

✅ Del 2 - xyz.html (10%)

<!doctype html>  <!-- Angir at dokumentet er et HTML-dokument (HTML5) -->
<html>  
  <head>
    <meta charset="utf-8">
    <title>XYZ</title>

    <!-- Referanse til en fil (xyz.css) som inneholder informasjon om
         hvordan denne websiden skal stilsettes. Stilinformasjonen i filen
         skal følge standarden Cascading Style Sheets (CSS) -->
    <link rel="stylesheet" type="text/css" href="xyz.css" />

    <!-- Referanse til en fil (xyz.js) som inneholder et skript som
    skal lastes, tolkes og kjøres. Koden i skriptet skal være kodet
    JavaScript, som er standard-skriptspråk -->
    <script src="xyz.js"> </script>

  </head>

  <body>

    <!-- Et skjema med et tekstfelt og en
    innsendingsknapp. Form-attributtene angir hvor skjemaet skal
    sendes (xyz.cgi) og hvilken HTTP-metode som skal brukes ved
    innsendingen (post). -->
    <form action='xyz.cgi' method='post'> 
      <input name='X' id='x' type='text'> 
      <input type='submit'>
    </form>

    <!-- En ekstra trykk-knapp som utløser kjøring av
    JavaScript-funksjonen xyz(). Denne funksjonen er definert i filen
    xyz.js -->
    <button type="button" onclick='xyz()'> fetch </button>

    <!-- En tom beholder beregnet for resultatet fra fetch-kallet i
    xyz() -->
    <div id='r'></div>

  </body>
</html>

✅ Del 3 - xyz.js (10%)

// funksjonen xyz() defineres
function xyz(){

    // Lager et URL-objekt til bruk i fetch-metoden
    let b = 'https://usn.no/';
    let u = new URL('xyz.cgi', b);

    // Lager referanser til elementer i html-dokumentet
    let t = document.querySelector('#x').value;
    let r = document.querySelector('#r');

    // Sender HTTP-post-forespørsel. Responsens lagres i
    // html-elementet r som er definert i xyz.html.
    fetch( u, { method: 'post', body: t } )
        .then( respons => respons.text() )
        .then( k => r.innerHTML += k )
}

✅ Del 4 - Forskjell mellom knappene (5%)

Dersom brukeren trykker på "button"-knappen (som er utenfor html-skjemaet), vil responsen fra tjeneren bli satt inn i den websiden som allerede eksisterer og vises i nettleseren.

Ved trykk på submit-knappen (inni skjemaet/"formen"), vil derimot websiden erstattes av responsen, slik at responsen danner en hel ny web-side som vises.

Oppgave 5 - Cookies i autentisering 15%

Forklar og vis med et eksempel hvordan informasjonskapsler (cookies) kan brukes i en autentiseringsmekanisme i et web-tjeneste (f.eks. et REST-API mot en database), slik at bare autentiserte brukere/prosesser får tilgang til tjenesten.

La forklaringen din dekke:

  • behandling på web-tjener,
  • behandling på web-klient og
  • hvordan informasjonskapslene blir transportert mellom dem.

✅ Sensorveiledning

Steg 1: Innlogging

Identitetsbevis (credentials) sendes fra nettleseren/web-klienten (K) til web-tjeneren (T).

Eksempel: K sender brukernavn og passord til T.

Steg 2: Autentisering

T avgjør om identitetsbeviset gir grunnlag for å autentisere K.

Eksempel: T hasher K's passord og sammenligner med passordhash som T tidligere har forsikret seg om at tilhører K. Dersom de to er like, er K autentisert.

Steg 3: Opprett sesjon

Dersom T autentiserte K, lages et bevis/tegn/token (i form av en tekst-streng) på at brukeren er autentisert. T sender dette beviset til K, som en del av HTTP-hodet, på en egen tekstlinje med et navn-verdi-par, pre-fikset med Set-cookie:.

Eksempel: T lager en universelt unik identifikator (UUID), 00fb9526-1c1f-4d86-ac6e-c4ded9010d5e, som K skal kunne bruke til å bevise at den er autentisert.

I HTTP-hodet til T's respons inkluderes følgende linje:

Set-cookie: sesjonsid=00fb9526-1c1f-4d86-ac6e-c4ded9010d5e

Steg 4: Påfølgende forespørsler

Når K mottar HTTP-responsen og tolker linjen som begynner med Set-cookie:, vil den lagre navn-verdi-paret sammenholdt med T's identitet. De påfølgende forespørsler mot T, vil inneholde dette beviset på at K er autentisert. Dette gjøres ved å sette det i forespørslenes HTTP-hode – på en egen linje som begynner med Cookie:.

Eksempel: I HTTP-hodet til K's forespørsler er følgende linje inkludert (inntil informasjonskapselen/cookien ikke lenger er gyldig):

Cookie: sesjonsid=00fb9526-1c1f-4d86-ac6e-c4ded9010d5e
Oppgave 6 - Serviceworkers 10%

Forklar/vis hvordan

  • serviceworker virker og
  • hvordan de kan brukes til å bidra til at web-applikasjoner kan virke uten internettforbindelse.

✅ Sensorveiledning

Hva er serviceworker?

Serviceworker er et programvareobjekt i nettleserene som er tilgjengelig for web-applikasjons-programmerere. De fungerer som en proxy mellom websiden og webtjeneren, slik at de kan avskjære og manipulere kommunikasjonen. De kan instrueres til å mellomlagre/cache HTTP-responser.

Hvordan brukes det for offline-funksjonalitet?

Dette kan utnyttes slik at når web-applikasjonen gjør en HTTP-forespørsel, blir den behandlet av serviceWorker'n før den faktisk blir sendt til tjeneren.

Den kan da være programmert til å:

  1. Sjekke i lokalt mellomlager/cache om det finnes en kopi der
  2. Velge å levere det som ligger der, som om det hadde kommet fra tjeneren
  3. Dette kan gjøres hver gang en tjeneren ikke er tilgjengelig – f.eks. når internettforbindelsen eller selve tjeneren/tjenesten er nede

Resultat: Web-applikasjonen kan fortsette å fungere selv uten internettforbindelse, ved å bruke cachede ressurser.

Oppgave 7 - Virtualisering & SOP 15%

Del 1 (10%):

  • Hva menes med operativsystem-nivå-virtualisering?
  • Nevn minst to eksempler på systemer for operativsystem-nivå-virtualisering.

Del 2 (5%):

  • Forklar/vis hva som menes med retningslinjen for samme opphav (same origin policy), og
  • forklar hvordan og hvor denne retningslinjen blir håndhevet.

✅ Del 1 - Operativsystem-nivå-virtualisering (10%)

Definisjon:

Et virtuelt system, som fremstår som en selvstendig vert settes opp med et sett av tilhørende ressurser. F.eks. filer, prosesser, nettverksenheter, tilkoblede maskinvareenheter, etc.

Dette gjøres ved å bruke avgrensede deler av vertsystemets eksisterende ressurser.

Konteinere vs Virtuelle maskiner:

Slike virtuelle systemer, som er skapt v.h.a. operativsystem-nivå-virtualisering, kalles som regel for konteinere. En vesentlig forskjell mellom konteinere og det som kalles virtuelle maskiner (VM), er at konteinerne deler operativsystemkjerne med vertssystemet.

Eksempler:

  • Docker
  • LXD (Linux Container)
  • Podman
  • Vi har også bygget konteinere manuelt ved hjelp av linux-kommandoene unshare og chroot

✅ Del 2 - Same Origin Policy (5%)

Definisjon:

Retningslinjen går ut på at nettlesere (web browsers) i utgangspunktet begrenser mulighetene for skript å kommunisere med fremmed kode – webtjenester/-tjenere som har et annet opphav (origin) enn skriptet selv.

Opphav bestemmes av:

  • Protokoll/skjema (f.eks. http)
  • Verten (f.eks. debbie.usn.no)
  • Porten (f.eks. 80)

Hvor og hvordan håndheves det?

Dette håndheves i rådende nettlesere, ved at kommunikasjon med fremmed kode kun tillates i visse tilfeller/unntak, som kan bestemmes/settes av utviklerne.

CORS (Cross-Origin Resource Sharing):

I emnet har vi sett på eksempler/oppgaver hvor vi har brukt mekanismen Cross-origin resource sharing (CORS), for å lage slike unntak. I CORS gir den fremmede tjeneren nettleseren tillatelser (av typen Access-Control-*).