DNS, proxy web, HTTP CONNECT e gateway applicativi
In questa pagina 8
HTTP non lavora da solo. Prima di aprire la connessione serve l'indirizzo del server (DNS); fra il client e il server possono trovarsi intermediari (proxy, cache, gateway) che inoltrano, filtrano o traducono i messaggi; e per passare attraverso un proxy con un protocollo cifrato come HTTPS serve il metodo CONNECT. Questa nota tratta questi servizi di supporto dal punto di vista di chi li programma. L'architettura gerarchica del DNS e la sua amministrazione sono in Livello applicazione - DNSIl DNS (Domain Name System) traduce i nomi (www.amazon.com) negli indirizzi IP, perché le persone preferiscono i nomi e i protocolli TCP/IP usano gli indirizzi. È un database distribuito e gerarchico: albero rovesciato con radice, domini di primo livello e sottodomini (al più 128 livelli); le informazioni sono su tanti server (13 server radice) e i nuovi domini si registrano presso un registrar accreditato ICANN. Ogni ISP ha un DNS locale, il cui indirizzo l'host riceve con DHCP: l'host (resolver) gli manda la richiesta, di solito su UDP, e il DNS locale interroga radice, dominio di primo livello e server dell'organizzazione. Ogni server che impara un'associazione la tiene in cache, marcando la risposta non autoritativa, e la scarta dopo il TTL. Record (nome, tipo, valore, classe, TTL). Con il NAT il DNS deve restituire l'indirizzo pubblico. Quattro attacchi: macchina compromessa, risposta falsa all'host, avvelenamento della cache del DNS locale, server DNS malevolo.Livello applicazione - DNS →; la cache lato HTTP in Caching, autenticazione, tipi MIME e URI in HTTPLa cache HTTP riusa le risposte senza contattare il server finche' sono fresche (Cache-Control: max-age, Expires; in mancanza una durata euristica pari a circa il 10% del tempo trascorso da Last-Modified) e, quando sono scadute, le rivalida con una richiesta condizionale (If-None-Match con ETag, If-Modified-Since con Last-Modified) alla quale il server risponde 304 senza corpo; no-store vieta di memorizzare, no-cache obbliga a rivalidare, private esclude le cache condivise. L'autenticazione usa 401 con WWW-Authenticate e la risposta Authorization: Basic (base64 di utente:password, solo sopra TLS) o Digest (hash con nonce); 407 vale per i proxy. Content-Type porta il tipo MIME (tipo/sottotipo; parametri come charset e boundary), Accept e gli altri header Accept-* guidano la negoziazione. Un URI (RFC 3986) e' scheme://userinfo@host:porta/percorso?query#frammento, con percent-encoding %HH per i byte non ammessi e regole per risolvere i riferimenti relativi.Caching, autenticazione, tipi MIME e URI in HTTP →.
Il DNS dal punto di vista del programma
Come un programma risolve un nome
Un programma non parla con il DNS direttamente: chiama getaddrinfo (System call POSIX, file descriptor e API delle socketLe system call sono le funzioni con cui un programma in user space chiede servizi al kernel (open, read, write, close, fork, pipe, dup2, socket, bind, listen, accept, connect); restituiscono -1 e impostano errno in caso di errore; un file descriptor e' un intero che indicizza la tabella dei file aperti del processo (0 stdin, 1 stdout, 2 stderr) e vale per file, pipe e socket; read e write possono trasferire MENO byte del richiesto (e sui socket TCP non c'e' nessun confine fra i messaggi), quindi servono cicli write_all e read_exact; l'API delle socket crea un punto finale di comunicazione (socket), lo lega a indirizzo e porta (bind), lo rende passivo (listen), accetta connessioni (accept) o si connette (connect); getaddrinfo traduce nomi e porte in indirizzi (IPv4 e IPv6); UDP usa sendto e recvfrom e conserva i confini dei datagrammi, TCP e' uno stream affidabile.System call POSIX, file descriptor e API delle socket →). La libreria consulta, nell'ordine configurato (/etc/nsswitch.conf), il file locale /etc/hosts e poi il DNS, inviando le domande al resolver ricorsivo indicato in /etc/resolv.conf (di solito quello del provider o della rete locale). Il resolver ricorsivo lavora per conto del client: se non ha la risposta in cache interroga in modo iterativo i server radice, quelli del dominio di primo livello e quelli autoritativi (Livello applicazione - DNSIl DNS (Domain Name System) traduce i nomi (www.amazon.com) negli indirizzi IP, perché le persone preferiscono i nomi e i protocolli TCP/IP usano gli indirizzi. È un database distribuito e gerarchico: albero rovesciato con radice, domini di primo livello e sottodomini (al più 128 livelli); le informazioni sono su tanti server (13 server radice) e i nuovi domini si registrano presso un registrar accreditato ICANN. Ogni ISP ha un DNS locale, il cui indirizzo l'host riceve con DHCP: l'host (resolver) gli manda la richiesta, di solito su UDP, e il DNS locale interroga radice, dominio di primo livello e server dell'organizzazione. Ogni server che impara un'associazione la tiene in cache, marcando la risposta non autoritativa, e la scarta dopo il TTL. Record (nome, tipo, valore, classe, TTL). Con il NAT il DNS deve restituire l'indirizzo pubblico. Quattro attacchi: macchina compromessa, risposta falsa all'host, avvelenamento della cache del DNS locale, server DNS malevolo.Livello applicazione - DNS →), e restituisce l'indirizzo. Il client dunque fa una sola domanda al suo resolver.
Strumenti per osservare: dig www.example.com (mostra header, sezioni e TTL), dig +trace (segue la gerarchia dalla radice), nslookup, host, getent hosts nome.
Il formato dei messaggi (RFC 1035)
Domande e risposte hanno lo stesso formato:
+---------------------+
| Header | 12 byte
+---------------------+
| Question | la domanda (QDCOUNT voci)
+---------------------+
| Answer | record di risposta (ANCOUNT)
+---------------------+
| Authority | server autoritativi (NSCOUNT)
+---------------------+
| Additional | informazioni extra (ARCOUNT)
+---------------------+| campo | bit | significato |
|---|---|---|
ID |
16 | identificatore scelto dal client; la risposta lo ripete |
| flag | 16 | QR (bit 15: 0 domanda, 1 risposta), Opcode (4 bit), AA (risposta autoritativa), TC (troncata), RD (ricorsione desiderata), RA (ricorsione disponibile), Z, RCODE (4 bit) |
QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT |
16 ciascuno | numero di voci nelle quattro sezioni |
Codici RCODE: 0 NOERROR, 1 FORMERR (messaggio malformato), 2 SERVFAIL (guasto del server), 3 NXDOMAIN (il nome non esiste), 4 NOTIMP, 5 REFUSED.
Domanda: QNAME, QTYPE, QCLASS. Il nome è scritto come sequenza di etichette, ciascuna preceduta da un byte con la sua lunghezza (massimo 63), terminata da un byte 0 (la radice): www.example.com diventa 03 'w' 'w' 'w' 07 'e' 'x' 'a' 'm' 'p' 'l' 'e' 03 'c' 'o' 'm' 00, cioè 17 byte. QTYPE dice che record si cerca, QCLASS è quasi sempre 1 (IN, Internet).
Record di risorsa (Resource Record, RR), usato nelle sezioni di risposta: NAME, TYPE (2 byte), CLASS (2), TTL (4 byte, secondi di validità in cache), RDLENGTH (2), RDATA (i dati, RDLENGTH byte).
Compressione dei nomi. Un nome già comparso nel messaggio non si ripete: un byte che inizia con i bit 11 indica un puntatore a 14 bit (i 6 bit restanti più il byte seguente) all'offset, contato dall'inizio del messaggio, dove il nome continua. Per esempio c0 0c significa "il nome si trova all'offset 12", cioè subito dopo l'header, dove sta il nome della domanda. Chi legge un nome deve seguire i puntatori (e proteggersi dai cicli: un puntatore a sé stesso).
Esempio reale. La domanda per l'indirizzo di www.example.com (tipo A, ID = 0x1234) è di 33 byte:
12 34 ID
01 00 flag: QR=0, Opcode=0, RD=1
00 01 00 00 00 00 00 00 QDCOUNT=1, ANCOUNT=NSCOUNT=ARCOUNT=0
03 77 77 77 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 QNAME www.example.com (17 byte)
00 01 QTYPE = A
00 01 QCLASS = INe la risposta è di 65 byte:
12 34 81 80 00 01 00 02 00 00 00 00 header: QR=1 RD=1 RA=1 RCODE=0, 1 domanda, 2 risposte
03 77 77 77 07 ... 63 6f 6d 00 00 01 00 01 la domanda ripetuta (21 byte)
c0 0c 00 01 00 01 00 00 01 17 00 04 68 14 17 9a risposta 1: nome = puntatore a 12; A; IN; TTL = 0x117 = 279 s;
4 byte: 104.20.23.154
c0 0c 00 01 00 01 00 00 01 17 00 04 ac 42 93 f3 risposta 2: ... 172.66.147.243Ogni risposta occupa 16 byte (2 di puntatore, 2 di tipo, 2 di classe, 4 di TTL, 2 di lunghezza, 4 di dato): 12 + 21 + 16 + 16 = 65. Due record A per lo stesso nome sono normali: il DNS è anche un modo per distribuire il carico (round robin) e per indirizzare l'utente al server più vicino (CDN).
Trasporto. Le domande usano UDP, porta 53: un datagramma di domanda, uno di risposta, senza handshake. Il limite classico di una risposta è di 512 byte; se non entra, il server la tronca e accende il bit TC: il client ripete la domanda su TCP porta 53 (con due byte di lunghezza davanti al messaggio). Estensioni moderne: EDNS0 per messaggi più lunghi su UDP, DNSSEC per le firme, DNS su TLS e su HTTPS. Poiché UDP non protegge, l'ID a 16 bit e la porta sorgente casuale sono le difese contro le risposte false (cache poisoning, Livello applicazione - DNSIl DNS (Domain Name System) traduce i nomi (www.amazon.com) negli indirizzi IP, perché le persone preferiscono i nomi e i protocolli TCP/IP usano gli indirizzi. È un database distribuito e gerarchico: albero rovesciato con radice, domini di primo livello e sottodomini (al più 128 livelli); le informazioni sono su tanti server (13 server radice) e i nuovi domini si registrano presso un registrar accreditato ICANN. Ogni ISP ha un DNS locale, il cui indirizzo l'host riceve con DHCP: l'host (resolver) gli manda la richiesta, di solito su UDP, e il DNS locale interroga radice, dominio di primo livello e server dell'organizzazione. Ogni server che impara un'associazione la tiene in cache, marcando la risposta non autoritativa, e la scarta dopo il TTL. Record (nome, tipo, valore, classe, TTL). Con il NAT il DNS deve restituire l'indirizzo pubblico. Quattro attacchi: macchina compromessa, risposta falsa all'host, avvelenamento della cache del DNS locale, server DNS malevolo.Livello applicazione - DNS →).
Un client DNS in C, con la domanda costruita a mano e la risposta decodificata, è in Esercizio - Client DNS con query e risposta costruite a mano (sul modello della prova pratica).
Web proxy
Definizione (proxy e gateway, RFC 9110 par. 3.7). Un intermediario è un nodo che sta fra client e server e inoltra i messaggi. Un proxy è un intermediario scelto dal client (configurato nel browser o nel sistema) che inoltra le richieste verso i server di origine. Un gateway, o reverse proxy, è un intermediario scelto dal server: per il client sembra il server di origine e inoltra le richieste ad altri sistemi.
| tipo | chi lo sceglie | il client sa di usarlo? | uso |
|---|---|---|---|
| forward proxy | il client (configurazione) | sì | filtraggio, cache, anonimato, accesso controllato a Internet |
| intercepting (transparent) proxy | la rete (reindirizzamento del traffico) | no | cache e filtri imposti |
| reverse proxy / gateway | il gestore del sito | no | bilanciamento, TLS, cache, sicurezza |
| tunnel | il client, con CONNECT |
sì | far passare protocolli cifrati |
Come funziona un forward proxy
Il client apre la connessione verso il proxy (non verso il server) e scrive nella request line l'URI assoluto (RFC 9112 par. 3.2.2):
GET http://www.example.com/index.html HTTP/1.1
Host: www.example.com
Proxy-Connection: keep-alive
User-Agent: curl/8.0Il proxy ricava l'host e la porta dall'URI, apre una connessione TCP verso il server di origine (usando il DNS), riscrive la richiesta in forma normale (GET /index.html HTTP/1.1, con Host), la inoltra, riceve la risposta e la gira al client. Se ha in cache una copia fresca risponde da solo. Il codice di un proxy semplice è in Esercizio - Proxy HTTP con filtro degli host (sul modello della prova pratica). Cosa deve fare un proxy corretto:
- Togliere gli header hop-by-hop. Alcuni header riguardano solo la connessione fra due nodi adiacenti e non devono attraversare il proxy:
Connection,Keep-Alive,Proxy-Connection(non standard),Proxy-Authenticate,Proxy-Authorization,TE,Trailer,Transfer-Encoding,Upgrade, più ogni header elencato dentroConnection. Gli altri sono end-to-end e passano. Il proxy gestisce con client e server due connessioni separate, con la propria persistenza. - Via. Aggiunge
Via: 1.1 nome-proxy(versione del protocollo e nome del nodo) a richieste e risposte: documenta il percorso e permette di rilevare i cicli (un proxy che si ritrova nelVia). - Indirizzo del client. Il server vede solo l'IP del proxy. L'header
X-Forwarded-For: 192.0.2.7(o ilForwardedstandard, RFC 7239) comunica l'indirizzo del client; è un'informazione dichiarata dal proxy, non verificabile. - Errori propri.
502 Bad Gatewayse la risposta del server è non valida o la connessione non riesce;504 Gateway Timeoutse il server non risponde in tempo;407 Proxy Authentication Requiredse il proxy richiede autenticazione (Proxy-Authenticate,Proxy-Authorization). - Cache. Un proxy è una cache condivisa: rispetta
Cache-Control(privateesclude,s-maxagevale per lui),Varye le richieste condizionali; non memorizza le risposte a richieste conAuthorizationse non sonopublic. - Limiti e sicurezza. Corpi e header con limiti di dimensione; un proxy aperto a tutti (open proxy) viene sfruttato per nascondere attacchi: serve autenticazione o restrizione agli indirizzi della rete.
Per i client: l'impostazione si fa nel browser, con le variabili http_proxy/https_proxy, oppure con curl -x http://proxy:8888 URL. Un intercepting proxy invece funziona senza configurazione (il firewall reindirizza la porta 80), ma non può farlo per HTTPS senza intercettare TLS con un proprio certificato.
Il vantaggio della cache condivisa si misura con il tempo medio di accesso: con probabilità di hit h, tempo di hit T_hit e di miss T_miss, T_medio = h T_hit + (1 - h) T_miss (Livello applicazione - HTTPIl livello applicazione è il più alto della pila: offre servizi all'utente con una connessione logica tra le due applicazioni e riceve servizi solo dal trasporto (DNS, HTTP, e-mail, FTP). Il Web (WWW, nato al CERN nel 1989) è un servizio client-server distribuito di pagine collegate da ipertesti; ogni pagina ha un URL protocollo://host:porta/percorso. HTTP: il client manda una richiesta, il server una risposta, su TCP (server sulla porta 80, client su una porta temporanea); senza stato. Messaggi di testo (riga di richiesta o di stato, intestazioni, riga vuota, corpo), metodi GET, POST, HEAD, PUT, DELETE, codici di stato 2xx-5xx. Una pagina con N oggetti incorporati richiede 2(N+1) RTT con connessioni non persistenti e (N+2) RTT con connessione persistente (trascurando la trasmissione). I cookie danno memoria al protocollo: Set-Cookie nella risposta, Cookie nelle richieste, file nel browser e base di dati nel sito. Un proxy (web cache) tiene le copie delle risposte recenti: meno carico sul server, meno traffico, meno ritardo.Livello applicazione - HTTP →).
Caching distribuito
Una sola cache ha capacità e banda limitate. Due soluzioni per scalare:
Gerarchie e cache cooperanti. Le cache si organizzano ad albero (cache dell'ufficio, cache del provider, cache nazionale: parent) e a pari livello (sibling). Un miss locale viene prima chiesto al padre o ai fratelli e solo poi al server. I fratelli si interrogano con protocolli leggeri su UDP come ICP (Internet Cache Protocol, RFC 2186: la risposta è HIT o MISS) o HTCP, oppure si scambiano riassunti del contenuto (cache digest).
Esempio. Hit locale 40% con 10 ms; dei miss la metà trova l'oggetto nella cache padre (40 ms), il resto va al server (200 ms). Tempo medio: 0,4 · 10 + 0,6 · (0,5 · 40 + 0,5 · 200) = 4 + 72 = 76 ms, contro 0,4 · 10 + 0,6 · 200 = 124 ms senza il livello intermedio.
Partizionare lo spazio degli URL. Con N cache in parallelo si assegna ogni URL a una cache con una funzione hash: cache = hash(URL) mod N. Così lo stesso oggetto non è duplicato e il client sa a quale cache rivolgersi. Se però N cambia (una cache si rompe o se ne aggiunge una), con il resto della divisione quasi tutti gli URL cambiano cache: passando da 4 a 5 cache, un URL resta nella stessa cache solo se k mod 4 = k mod 5, cioè in 4 casi su 20, e l'80% dei contenuti va rimesso in cache. La soluzione è l'hashing coerente (consistent hashing, molto usato nei sistemi distribuiti; il protocollo CARP delle cache proxy ottiene lo stesso effetto con una variante, highest random weight): cache e URL sono posti su uno stesso anello di valori hash e ogni URL va alla prima cache che segue sull'anello; aggiungendo o togliendo una cache si spostano solo gli URL del tratto adiacente, in media 1/N del totale (20% con 5 cache).
CDN. Una content delivery network è una rete di cache (edge server) distribuite nel mondo: il sito delega i contenuti, e il DNS del sito risponde con l'indirizzo dell'edge più vicino al client (l'host www.sito.it è un CNAME verso il nome della CDN, che a sua volta risponde in base alla posizione del resolver). La latenza scende perché il contenuto è vicino e il server di origine è alleggerito.
HTTP CONNECT e i tunnel
Un proxy che legge le richieste HTTP non può farlo con HTTPS: il traffico è cifrato con TLS fra browser e server (Crittografia asimmetrica, RSA e TLSCrittografia asimmetrica (a chiave pubblica): chiave di cifratura pubblica v_B, chiave di decifratura privata s_B, matematicamente legate; C = E_vB(P), P = D_sB(C); dà riservatezza. I certificati digitali, emessi da una Certificate Authority e firmati con la sua chiave privata, legano un'identità a una chiave pubblica. Firma digitale: hash del messaggio cifrato con la chiave privata; garantisce autenticità, integrità e non ripudio. RSA: si scelgono due primi grandi p e q, N = pq, phi(N) = (p-1)(q-1), e coprimo con phi(N), d = e^(-1) mod phi(N); chiave pubblica (N, e), segreta (N, d); cifratura c = m^e mod N, decifratura m = c^d mod N con m < N (teorema di Eulero); sicura finché la fattorizzazione è difficile (N di almeno 2048 bit); il padding casuale (PKCS#1 v1.5: 00 02 [casuale] 00 [m]) difende da malleabilità e determinismo. La firma RSA è s = m^d mod N, verificata con s^e mod N. TLS (su TCP) autentica gli estremi con il certificato, cifra i dati con una chiave di sessione simmetrica e garantisce l'integrità con i MAC; da TLS 1.0 a 1.3 la chiave si ricava con Diffie-Hellman e l'handshake si accorcia. DTLS è la versione per UDP.Crittografia asimmetrica, RSA e TLS →). Il client chiede allora al proxy di fare da semplice tunnel: il metodo CONNECT (RFC 9110 par. 9.3.6).
Definizione (CONNECT).
CONNECT host:porta HTTP/1.1: chiede al proxy di aprire una connessione TCP versohost:portae, se ci riesce, di inoltrare senza interpretarli i byte in entrambe le direzioni finché una delle due parti chiude. Il request-target è in authority-form: solo host e porta, e la porta è obbligatoria.
CONNECT www.example.com:443 HTTP/1.1
Host: www.example.com:443
Proxy-Authorization: Basic bWFyaW86c2VncmV0bw==Se il proxy riesce a collegarsi risponde con un qualunque 2xx:
HTTP/1.1 200 Connection establishede da quel momento la connessione non è più HTTP: non ci sono Content-Length né Transfer-Encoding (il server non deve mandarli e il client deve ignorarli se li riceve) e quello che il client scrive è il TLS del server di origine.
browser proxy server (www.example.com:443)
|-- CONNECT www.example.com:443 -->|
| |------ connessione TCP ------>|
|<-- 200 Connection established ---|
|================ tunnel: byte inoltrati in entrambe le direzioni ==============|
|-- ClientHello (TLS) ------------>|----------------------------->|
|<-- ServerHello, certificato -----|<-----------------------------|
|== richiesta HTTP cifrata ========|==============================>|Il proxy vede solo a chi ci si collega (host e porta) e quanti byte passano, non il contenuto, a meno che intercetti TLS presentandosi con un proprio certificato (e allora il browser deve fidarsi della sua CA). Gli errori prima del tunnel sono normali risposte HTTP: 403 Forbidden (vietato), 407 (serve autenticazione), 502 Bad Gateway, 504 Gateway Timeout.
struct pollfd pf[2] = { {.fd = client, .events = POLLIN}, {.fd = server, .events = POLLIN} };
for (;;) {
poll(pf, 2, 60000); /* 60 s di silenzio: si chiude */
for (int i = 0; i < 2; i++)
if (pf[i].revents & (POLLIN | POLLHUP | POLLERR)) {
ssize_t r = read(pf[i].fd, buf, sizeof buf);
if (r <= 0) { shutdown(pf[1 - i].fd, SHUT_WR); /* EOF: half-close dell'altro lato */ }
else write_all(pf[1 - i].fd, buf, r);
}
}Quando un lato smette di scrivere (EOF) si fa shutdown(altro, SHUT_WR) per propagare la chiusura, ma si continua a trasferire nell'altra direzione finché anche l'altro lato ha finito (half-close). Un tunnel dura a lungo, quindi il proxy usa un processo (o un thread, o un ciclo di eventi) per tunnel (Client e server TCP in C - bind, listen, accept e processi concorrentiUn client TCP in C fa getaddrinfo, socket, connect, poi write e read; un server fa socket, setsockopt(SO_REUSEADDR), bind, listen e un ciclo di accept. Un server iterativo serve un client alla volta e un client lento blocca tutti; un server concorrente con fork crea un processo figlio per ogni connessione: il figlio chiude il socket di ascolto e serve la connessione, il padre chiude il socket di connessione e torna ad accept. I figli terminati diventano zombie finche' il padre non li raccoglie con waitpid, di solito in un gestore di SIGCHLD con WNOHANG in un ciclo; i descrittori non chiusi tengono viva la connessione (nessun FIN) o esauriscono EMFILE; uno stato CLOSE_WAIT che si accumula indica una close mancante. Alternative: thread, pre-fork, I/O multiplexing con poll o epoll.Client e server TCP in C - bind, listen, accept e processi concorrenti →).
Sicurezza. CONNECT verso porte qualsiasi trasforma il proxy in un ponte verso qualunque servizio (posta, SSH, servizi interni): un attaccante potrebbe usare CONNECT mail.example.com:25 per inviare spam o CONNECT 10.0.0.5:22 per raggiungere macchine della rete interna (SSRF). Un proxy deve limitare le porte (di solito solo 443, a volte 80 e 8443), eventualmente gli host, richiedere autenticazione e registrare i collegamenti (Esercizio - Proxy con tunnel CONNECT (sul modello della prova pratica)). HTTP/2 e HTTP/3 definiscono un CONNECT analogo che apre un tunnel su un singolo flusso.
Gateway applicativi
Un gateway applicativo (application-layer gateway) è un intermediario che opera al livello applicazione e fa da ponte fra due sistemi: per il client è il server, per il sistema a valle è un client. Il caso più importante oggi è il reverse proxy, che sta davanti a uno o più server:
Internet reverse proxy rete interna
client ---- HTTPS ----> [ TLS, cache, filtri ] ---- HTTP ----> server applicativi 1, 2, 3Funzioni tipiche:
- bilanciamento del carico fra più server (round robin, meno connessioni attive, hash dell'URL o dell'IP del client), con controlli di salute e affinità di sessione;
- terminazione TLS: il gateway gestisce certificati e cifratura, e i server dietro parlano HTTP semplice;
- cache e compressione dei contenuti statici;
- sicurezza: filtri, web application firewall, limiti sul numero di richieste (rate limiting), nascondere la rete interna;
- instradamento per percorso o per host:
/api/...a un servizio,/immagini/...a un altro (API gateway, microservizi); - traduzione di protocolli: da HTTP a FastCGI o a un'applicazione CGI (CGI e applicazioni web dinamicheLa Common Gateway Interface (CGI, RFC 3875) e' l'interfaccia fra un server web e un programma esterno che genera la risposta: per ogni richiesta il server fa fork ed exec del programma, gli passa i dati con le meta-variabili d'ambiente (REQUEST_METHOD, QUERY_STRING, CONTENT_LENGTH, CONTENT_TYPE, SCRIPT_NAME, PATH_INFO, SERVER_*, REMOTE_ADDR, HTTP_* per gli header) e con lo standard input (corpo della POST, CONTENT_LENGTH byte); il programma scrive sullo standard output header CGI (Content-Type obbligatorio, Status, Location), una riga vuota e il corpo, e il server li trasforma in una risposta HTTP completa; i punti delicati sono pipe e dup2, la chiusura delle estremita' inutilizzate, il timeout, i limiti di dimensione e la sicurezza (injection, path traversal, header Proxy, shellshock); il costo di un processo per richiesta ha portato a FastCGI, ai moduli del server e ai server applicativi.CGI e applicazioni web dinamiche →), da HTTP a WebSocket (WebSocket, QUIC e HTTP-3HTTP e' richiesta e risposta, quindi poco adatto a notifiche e dati in tempo reale (polling, long polling, SSE); WebSocket (RFC 6455) apre con un handshake HTTP/1.1 Upgrade (Sec-WebSocket-Key a 16 byte casuali in Base64, risposta 101 con Sec-WebSocket-Accept = Base64 di SHA-1 di chiave piu' GUID fisso) un canale bidirezionale persistente sulla stessa connessione TCP, con frame di 2-14 byte di intestazione (FIN, opcode, MASK, lunghezza su 7, 16 o 64 bit, chiave di mascheramento obbligatoria dal client al server) e messaggi di testo, binari, close, ping e pong. QUIC (RFC 9000) e' un protocollo di trasporto sopra UDP con TLS 1.3 integrato, flussi indipendenti (niente head-of-line blocking fra flussi), connection ID che permettono di cambiare rete, handshake in 1 RTT e ripresa in 0 RTT; HTTP/3 (RFC 9114) mappa HTTP su QUIC con un flusso per richiesta e QPACK per gli header, ed e' annunciato con Alt-Svc.WebSocket, QUIC e HTTP-3 →), da HTTP/2 a HTTP/1.1.
Un reverse proxy si comporta come un forward proxy per quanto riguarda gli header (toglie gli hop-by-hop, aggiunge Via e X-Forwarded-For/X-Forwarded-Proto, sceglie Host) e usa gli stessi errori (502, 503, 504). Lo stesso schema con altri protocolli: un gateway di posta (SMTP verso un altro sistema), un ALG (application-level gateway) in un firewall o in un NAT, che deve riscrivere gli indirizzi dentro il payload di protocolli come FTP (NAT e indirizzi privatiUna rete privata (intranet) usa il protocollo TCP/IP con indirizzi privati ($10.0.0.0/8$, $172.16.0.0/12$, $192.168.0.0/16$), riutilizzabili da intranet diverse ma da non instradare in Internet: i router di bordo scartano i pacchetti con indirizzi privati. Per accedere a Internet servono un proxy applicativo (uno per applicazione) o il NAT (Network Address Translation): un router che traduce indirizzi privati in indirizzi pubblici di un pool, con una tabella NAT e un'associazione dinamica per sessione. Il NAT tradizionale è outbound: Basic NAT traduce solo l'IP (uno a uno, quindi servono tanti indirizzi pubblici quante le sessioni contemporanee), NAPT traduce anche la porta ([IP privato, porta] $\to$ [IP pubblico, porta del NAT]) e permette a molte sessioni di condividere un solo IP pubblico. Il Twice NAT permette sessioni anche dall'esterno, con un DNS interno e associazioni statiche.NAT e indirizzi privati →, FirewallUn firewall è un componente (hardware, software o entrambi) che blocca il traffico non autorizzato da una rete verso un'altra: filtra i dati, reindirizza il traffico, protegge dagli attacchi. Requisiti: tutto il traffico tra due zone di fiducia deve passare dal firewall, passa solo il traffico autorizzato, il firewall stesso deve essere immune alle intrusioni. L'amministratore configura le politiche (controllo di utente, di servizio, di direzione); un pacchetto può essere accettato, negato o rifiutato (negato più messaggio ICMP alla sorgente). Il firewall decide con le informazioni di intestazione dei livelli 2-4 (indirizzi, protocollo, porte, flag SYN/ACK/FIN). Packet filter: senza stato, filtra in ingresso e in uscita con regole su indirizzi, protocollo, porte, flag (esempi SMTP, Telnet, X11 e correzioni con porte sorgente e flag ACK). Stateful firewall: tiene traccia delle connessioni (NEW, ESTABLISHED, RELATED, INVALID), limita connessioni al secondo, timeout. Firewall applicativo/proxy: ispeziona il livello applicazione, apre una connessione separata verso la destinazione; sicuro ma lento. Linux: netfilter con 5 hook IPv4 e 5 verdetti, iptables (tabelle e catene), connection tracking nf_conntrack.Firewall →).
| intermediario | livello | chi lo sceglie | cosa fa |
|---|---|---|---|
| NAT, router | rete | la rete | inoltra o traduce gli indirizzi dei pacchetti |
tunnel (CONNECT) |
trasporto | il client | inoltra i byte senza capirli |
| forward proxy | applicazione | il client | capisce HTTP, filtra, mette in cache |
| reverse proxy / gateway | applicazione | il server | capisce HTTP, bilancia, traduce, protegge |
Esercizi collegati
- Esercizio - Client DNS con query e risposta costruite a mano (sul modello della prova pratica): formato RFC 1035, compressione dei nomi, UDP e ripiego su TCP.
- Esercizio - Proxy HTTP con filtro degli host (sul modello della prova pratica): URI assoluto, header hop-by-hop,
Via,X-Forwarded-For. - Esercizio - Proxy con tunnel CONNECT (sul modello della prova pratica):
CONNECT,poll, half-close, porte consentite. - Esercizio - Server HTTP con CGI (sul modello della prova pratica): un server come gateway verso un programma.
Errori tipici
- Dimenticare di convertire
QDCOUNT,ANCOUNT,IDda e verso big endian; leggere un nome senza seguire i puntatori di compressione, o seguirli senza limite di salti. - Scrivere nel messaggio DNS la lunghezza in decimale o in testo (
3www7example3com0): sono byte, non caratteri3e7. - Credere che il DNS usi solo UDP: le risposte troncate e i trasferimenti di zona usano TCP.
- Inoltrare nel proxy gli header hop-by-hop (
Connection,Proxy-Connection,Transfer-Encoding) e lasciare l'URI assoluto verso il server di origine. - Dimenticare che il server di origine vede l'IP del proxy: serve
X-Forwarded-For. - Nel tunnel
CONNECT: aspettarsiContent-Lengthnella risposta200; chiudere i due socket all'EOF di un lato perdendo i dati dell'altro; accettare qualsiasi porta di destinazione. - Usare
CONNECTcome se il proxy cifrasse: il proxy inoltra byte, il TLS è fra client e server. - Confondere forward proxy (scelto dal client) e reverse proxy (scelto dal server).
Domande d'esame
1. Descrivere la struttura di un messaggio DNS e dire come si codifica www.example.com nella domanda; cos'è la compressione dei nomi?
Traccia. Header di 12 byte (ID, flag con QR, RD, RA, TC e RCODE, quattro contatori), poi Question, Answer, Authority, Additional. www.example.com = 03 www 07 example 03 com 00 (17 byte), seguito da QTYPE e QCLASS da 2 byte. Compressione: un nome già comparso si sostituisce con un puntatore a 14 bit (byte che inizia con 11, es. c0 0c = offset 12) all'offset dove il nome comincia; il lettore segue i puntatori con un limite di salti.
2. Un browser deve raggiungere https://www.example.com passando per un proxy. Descrivere i messaggi e cosa vede il proxy.
Traccia. Il browser invia CONNECT www.example.com:443 HTTP/1.1 (authority-form, porta obbligatoria); il proxy apre la connessione TCP verso il server e risponde 200 Connection established (senza Content-Length); da lì inoltra i byte in entrambe le direzioni; il browser esegue l'handshake TLS e la richiesta HTTP cifrata con il server. Il proxy vede host e porta e il volume dei dati, non il contenuto (salvo interception TLS). Deve limitare le porte e richiedere autenticazione.
3. Un forward proxy riceve GET http://www.example.com/a.html HTTP/1.1 con Connection: keep-alive e Proxy-Connection: keep-alive. Cosa invia al server?
Traccia. Apre la connessione verso www.example.com:80; invia GET /a.html HTTP/1.1 con Host: www.example.com; non inoltra Connection né Proxy-Connection (hop-by-hop; imposta il proprio Connection); aggiunge Via: 1.1 proxy e X-Forwarded-For: <IP client>; poi gira la risposta al client, con i propri Via e le proprie regole di persistenza.
Versione ripasso
- Risoluzione. Il programma chiama
getaddrinfo:/etc/hosts, poi il resolver ricorsivo di/etc/resolv.conf, che interroga radice, TLD, autoritativi (Livello applicazione - DNSIl DNS (Domain Name System) traduce i nomi (www.amazon.com) negli indirizzi IP, perché le persone preferiscono i nomi e i protocolli TCP/IP usano gli indirizzi. È un database distribuito e gerarchico: albero rovesciato con radice, domini di primo livello e sottodomini (al più 128 livelli); le informazioni sono su tanti server (13 server radice) e i nuovi domini si registrano presso un registrar accreditato ICANN. Ogni ISP ha un DNS locale, il cui indirizzo l'host riceve con DHCP: l'host (resolver) gli manda la richiesta, di solito su UDP, e il DNS locale interroga radice, dominio di primo livello e server dell'organizzazione. Ogni server che impara un'associazione la tiene in cache, marcando la risposta non autoritativa, e la scarta dopo il TTL. Record (nome, tipo, valore, classe, TTL). Con il NAT il DNS deve restituire l'indirizzo pubblico. Quattro attacchi: macchina compromessa, risposta falsa all'host, avvelenamento della cache del DNS locale, server DNS malevolo.Livello applicazione - DNS →). Strumenti:dig,dig +trace,nslookup. - Messaggio DNS (RFC 1035). Header 12 byte (big endian):
ID, flag (QRbit 15,AA,TC,RD,RA,RCODE4 bit: 0 NOERROR, 2 SERVFAIL, 3 NXDOMAIN),QDCOUNT,ANCOUNT,NSCOUNT,ARCOUNT. Sezioni: Question, Answer, Authority, Additional. Domanda:QNAME(etichette con byte di lunghezza, max 63, terminato da00),QTYPE,QCLASS=1 (IN). RR:NAME,TYPE,CLASS,TTL(4 byte),RDLENGTH,RDATA. - Tipi.
A=1 (4 byte),NS=2,CNAME=5,SOA=6,PTR=12,MX=15 (preferenza + nome),TXT=16,AAAA=28 (16 byte). - Compressione. Byte con bit
11: puntatore a 14 bit all'offset da inizio messaggio (c0 0c= offset 12); limite di salti contro i cicli. - Esempio.
www.example.comA: domanda 33 byte (12 34 01 00 00 01 ..., QNAME 17 byte,00 01 00 01), risposta 65 byte (81 80, 2 risposte da 16 byte conc0 0c, TTL0x117= 279,104.20.23.154e172.66.147.243). - Trasporto. UDP porta 53 (limite classico 512 byte); bit
TC-> ripetere su TCP (2 byte di lunghezza davanti). EDNS0, DNSSEC, DoT/DoH. Difese anti-spoofing:IDa 16 bit e porta sorgente casuale. - Proxy. Intermediario scelto dal client (forward). Gateway/reverse proxy scelto dal server. Tunnel:
CONNECT. Il forward proxy riceve l'URI assoluto, apre la connessione al server, riscrive la richiesta in forma normale (conHost), gira la risposta (Livello applicazione - HTTPIl livello applicazione è il più alto della pila: offre servizi all'utente con una connessione logica tra le due applicazioni e riceve servizi solo dal trasporto (DNS, HTTP, e-mail, FTP). Il Web (WWW, nato al CERN nel 1989) è un servizio client-server distribuito di pagine collegate da ipertesti; ogni pagina ha un URL protocollo://host:porta/percorso. HTTP: il client manda una richiesta, il server una risposta, su TCP (server sulla porta 80, client su una porta temporanea); senza stato. Messaggi di testo (riga di richiesta o di stato, intestazioni, riga vuota, corpo), metodi GET, POST, HEAD, PUT, DELETE, codici di stato 2xx-5xx. Una pagina con N oggetti incorporati richiede 2(N+1) RTT con connessioni non persistenti e (N+2) RTT con connessione persistente (trascurando la trasmissione). I cookie danno memoria al protocollo: Set-Cookie nella risposta, Cookie nelle richieste, file nel browser e base di dati nel sito. Un proxy (web cache) tiene le copie delle risposte recenti: meno carico sul server, meno traffico, meno ritardo.Livello applicazione - HTTP →). - Header. Tolti gli hop-by-hop:
Connection,Keep-Alive,Proxy-Connection,Proxy-Authenticate,Proxy-Authorization,TE,Trailer,Transfer-Encoding,Upgrade(e quelli elencati inConnection). AggiuntiVia: 1.1 nome(cicli) eX-Forwarded-For/Forwarded(IP del client). Errori:502,504,407. Il proxy è una cache condivisa (private,s-maxage,Vary). - Cache distribuite. Gerarchie parent/sibling con ICP (
HIT/MISS) o HTCP, cache digest. Esempio:T = 0,4·10 + 0,6·(0,5·40 + 0,5·200) = 76 mscontro 124 ms. Partizione degli URL conhash(URL) mod N: seNcambia (4 -> 5) si muove l'80% degli URL; consistent hashing (anello): si sposta ~`1/N`. CDN: edge server vicini, scelti dal DNS (CNAME verso la CDN). - CONNECT.
CONNECT host:porta HTTP/1.1(authority-form, porta obbligatoria); successo = qualunque2xx(200 Connection established), senzaContent-Length/Transfer-Encoding; poi tunnel di byte (TLS fra client e server: il proxy non vede il contenuto). Errori prima del tunnel:403,407,502,504. - Tunnel in C.
pollsu client e server,read->writeverso l'altro; a EOFshutdown(altro, SHUT_WR)(half-close) e si continua nell'altra direzione; timeout di inattività; un processo per tunnel. - Sicurezza CONNECT. Limitare le porte (443), host e utenti; autenticazione; log. Altrimenti ponte verso SMTP, SSH, rete interna (SSRF).
- Gateway / reverse proxy. Bilanciamento (round robin, least connections, hash), terminazione TLS, cache, compressione, WAF, rate limiting, instradamento per percorso (API gateway), traduzione (HTTP -> FastCGI/CGI, WebSocket),
X-Forwarded-*. ALG: riscrive gli indirizzi nel payload (FTP) in NAT e firewall (NAT e indirizzi privatiUna rete privata (intranet) usa il protocollo TCP/IP con indirizzi privati ($10.0.0.0/8$, $172.16.0.0/12$, $192.168.0.0/16$), riutilizzabili da intranet diverse ma da non instradare in Internet: i router di bordo scartano i pacchetti con indirizzi privati. Per accedere a Internet servono un proxy applicativo (uno per applicazione) o il NAT (Network Address Translation): un router che traduce indirizzi privati in indirizzi pubblici di un pool, con una tabella NAT e un'associazione dinamica per sessione. Il NAT tradizionale è outbound: Basic NAT traduce solo l'IP (uno a uno, quindi servono tanti indirizzi pubblici quante le sessioni contemporanee), NAPT traduce anche la porta ([IP privato, porta] $\to$ [IP pubblico, porta del NAT]) e permette a molte sessioni di condividere un solo IP pubblico. Il Twice NAT permette sessioni anche dall'esterno, con un DNS interno e associazioni statiche.NAT e indirizzi privati →). - Codice essenziale (le funzioni centrali, senza commenti):
static int dns_read_name(const uint8_t *m, size_t len, size_t *pos, char *out, size_t outsz)
{
size_t p = *pos, o = 0;
int jumped = 0, hops = 0;
for (;;) {
if (p >= len)
return -1;
uint8_t l = m[p];
if (l == 0) {
p++;
break;
}
if ((l & 0xC0) == 0xC0) {
if (p + 1 >= len)
return -1;
size_t off = (size_t)(l & 0x3F) << 8 | m[p + 1];
if (!jumped)
*pos = p + 2;
jumped = 1;
if (off >= len || ++hops > 16)
return -1;
p = off;
continue;
}
if (l & 0xC0)
return -1;
p++;
if (p + l > len || o + l + 2 > outsz)
return -1;
if (o > 0)
out[o++] = '.';
memcpy(out + o, m + p, l);
o += l;
p += l;
}
out[o] = '\0';
if (o == 0)
snprintf(out, outsz, ".");
if (!jumped)
*pos = p;
return 0;
}static int is_hop_by_hop(const char *name)
{
static const char *hop[] = {"Connection", "Proxy-Connection", "Keep-Alive", "Proxy-Authenticate",
"Proxy-Authorization", "TE", "Trailer", "Transfer-Encoding", "Upgrade"};
for (size_t i = 0; i < sizeof hop / sizeof hop[0]; i++)
if (strcasecmp(name, hop[i]) == 0)
return 1;
return 0;
}- Errori tipici: interi DNS non convertiti da/in big endian; lunghezze delle etichette scritte come testo; puntatori di compressione ignorati; hop-by-hop inoltrati; URI assoluto verso l'origine;
Content-Lengthatteso nel200di CONNECT; chiusura di entrambi i lati all'EOF di uno; porte di CONNECT non limitate; forward e reverse proxy confusi.