Reti e web
Come funziona HTTP: richieste, risposte, header e codici di stato
Leggi uno scambio HTTP reale riga per riga: metodi, URL, header, classi di codici di stato e cookie, provati con curl su un server locale.
Una porta è un ingresso numerato della tua macchina. Come TCP e UDP usano le porte e come avviare, trovare e testare un server locale su 127.0.0.1.
Una porta è un numero che dice al sistema operativo quale programma deve ricevere una parte del traffico di rete. La tua macchina ha un indirizzo IP e può far girare decine di programmi di rete contemporaneamente: un browser che scarica pagine, un client di posta, un database, un web server locale. L'indirizzo IP porta il traffico al computer giusto. La porta lo consegna al programma giusto.
Se hai letto cosa fa il DNS, sai già come un nome diventa un indirizzo. Questo articolo spiega da dove vengono le porte, come le usano i due principali protocolli di trasporto e come vedere su quali porte è in ascolto la tua macchina. Il laboratorio gira su 127.0.0.1, l'indirizzo di loopback che significa sempre "questa macchina", quindi nulla esce dal tuo computer.
Una porta è un numero a 16 bit, quindi va da 0 a 65535. Gli header TCP e UDP contengono entrambi una porta di origine e una porta di destinazione. Quando un pacchetto raggiunge il tuo computer, il sistema operativo legge la porta di destinazione e consegna i dati al programma che ha chiesto di ricevere su quel numero.
Pensa a un condominio. L'indirizzo stradale fa arrivare la consegna al palazzo. Il numero dell'interno dice al corriere a quale porta bussare. In questa immagine l'indirizzo IP è il palazzo e la porta è l'interno. Qualcuno decide quale interno è quale, e un inquilino può andarsene e lasciare il numero a un altro.
Una porta in ascolto è una porta per cui un programma ha detto al sistema operativo, in sostanza, "manda a me le connessioni per questo numero". Un programma in questo stato è in ascolto. Quando il programma termina, il sistema operativo libera la porta e lì non risponde più nessuno.
Ogni servizio che esegui apre almeno una porta. Questo vale anche per programmi che non hai avviato di proposito: strumenti di aggiornamento, client di sincronizzazione, server di sviluppo, plugin e software che si installa come servizio in background. L'elenco delle porte in ascolto è il modo più semplice di vedere con precisione cosa offre la tua macchina alla rete.
Se controlli un host tuo, quell'elenco è la prima cosa da annotare. Prima di chiederti se un servizio ha le patch o è configurato bene, devi sapere che esiste. Il lavoro di sicurezza comincia dall'inventario, e l'inventario comincia dalle porte.
TCP/IP è la suite di protocolli su cui gira internet. È organizzata in livelli, e per ora non ti servono tutti. Qui contano quattro livelli:
Ogni livello avvolge i dati del livello superiore nel proprio header. L'header IP contiene gli indirizzi di origine e destinazione. L'header TCP o UDP contiene le porte di origine e destinazione. I dati dell'applicazione stanno all'interno. Quando il pacchetto arriva, ogni livello toglie il proprio header e passa il resto verso l'alto.
La porta sta nel livello di trasporto. IP non sa quale programma deve ricevere i dati. Quel compito spetta al livello di trasporto, ed è per questo che un solo indirizzo IP può servire una pagina web sulla porta 443 e un database sulla porta 5432 nello stesso momento.
TCP, specificato nella RFC 9293, dà a un programma un flusso di byte affidabile e ordinato. Prima che si muovano dati dell'applicazione, le due parti si accordano sui numeri di sequenza iniziali e confermano che ciascuna può inviare e ricevere. Questo accordo è il three-way handshake.
Quando nessun programma è in ascolto sulla porta, la macchina di destinazione non invia un SYN-ACK. Invia un segmento RST (reset), e il client rinuncia subito con un messaggio come "connection refused". Questa risposta rapida è utile. Ti dice che l'host ha ricevuto il pacchetto e che nessuno accetta connessioni su quella porta.
Un'attesa silenziosa che finisce in timeout significa altro. Molto spesso un firewall ha scartato i pacchetti senza rispondere. Quindi "refused" e "timed out" sono due indizi diversi. Impara a leggerli.
Le connessioni su 127.0.0.1 seguono la stessa logica TCP anche se i pacchetti non arrivano mai a una scheda di rete. Il sistema operativo esegue l'handshake via software, e questo rende l'indirizzo di loopback un posto sicuro per esercitarsi.
UDP, specificato nella RFC 768, è molto più semplice. Il suo header ha 8 byte: porta di origine, porta di destinazione, lunghezza e checksum. Non ci sono handshake, conferme, ritrasmissioni né ordinamento. Il mittente trasmette un datagramma, e la rete lo consegna oppure no. Il destinatario non conferma nulla, a meno che l'applicazione non implementi da sé questa logica.
Questa semplicità ha i suoi usi. Le query DNS viaggiano di solito su UDP, porta 53, perché una domanda breve e una risposta breve stanno in un solo scambio, e un handshake aggiungerebbe viaggi di andata e ritorno. Voce e video in tempo reale usano spesso UDP, e lo stesso fa QUIC, il trasporto sotto HTTP/3. Ognuno di questi protocolli ha le proprie regole per perdite e ritrasmissioni.
Per UDP le porte funzionano allo stesso modo. La porta di destinazione sceglie il programma. La differenza è che una porta UDP non ha stato di connessione. Qualsiasi programma associato a quella porta riceve tutto ciò che arriva, da chiunque lo invii. Un listener TCP vede solo le connessioni che completano l'handshake. Entrambi rispondono comunque alla stessa domanda: quale programma riceve questo pacchetto.
La Internet Assigned Numbers Authority (IANA) gestisce il Service Name and Transport Protocol Port Number Registry. Il registro indica quale servizio è comunemente associato a ogni numero di porta per TCP, UDP e pochi altri protocolli di trasporto. Tra le voci più note ci sono 22 per ssh, 53 per domain (DNS), 80 per http e 443 per https.
La RFC 6335 divide lo spazio dei numeri in tre intervalli:
Il registro riporta assegnazioni e convenzioni. Né TCP né UDP le impongono. Qualsiasi programma con i permessi necessari può associarsi a quasi ogni porta libera. Il registro ti dice cosa gira di solito su un numero, e sulla tua macchina può sbagliare. Guarda cosa è davvero in ascolto prima di trarre una conclusione.
Ti servono un terminale e Python 3. Il laboratorio usa il server HTTP integrato in Python, quindi non devi installare altro.
Ho eseguito i comandi per Windows qui sotto su Windows 11 Pro con Git Bash, Python 3.12.10 e curl 8.12.1. Non ho eseguito i comandi per Linux e macOS su questa macchina, quindi il loro output non è riportato. Dove compare un blocco di output, viene dal comando mostrato sopra.
Una nota su Python: nella mia sessione Git Bash python3 non veniva trovato, mentre python lanciava Python 3.12. Se su Windows python3 non funziona, usa python. Il resto del comando non cambia.
Il server pubblica tutto quello che si trova nella cartella da cui lo avvii, quindi prima mettici qualcosa di piccolo.
mkdir -p ~/port-lab && cd ~/port-lab
printf '<!doctype html><title>lab</title><h1>Port 8000 lab</h1>\n' > index.htmlSu PowerShell, crea la stessa cartella e scrivi quelle due righe in index.html con un editor qualsiasi.
Tieni aperto questo terminale. Il server gira finché non lo fermi.
cd ~/port-lab
python3 -m http.server 8000 --bind 127.0.0.1Il flag --bind 127.0.0.1 dice a Python di ascoltare solo sull'interfaccia di loopback. Senza il flag, Python ascolta su ogni interfaccia della macchina. Questa differenza conterà più avanti, quando leggerai l'elenco delle porte in ascolto.
Scegli il comando per il tuo sistema operativo. Ognuno elenca i socket in stato LISTEN che usano la porta 8000.
Linux:
ss -tlnp | grep 8000macOS:
lsof -iTCP -sTCP:LISTEN -n -P | grep 8000Windows PowerShell:
Get-NetTCPConnection -State Listen -LocalPort 8000Questo è l'output che ho ottenuto dal comando Windows sulla mia macchina, passato attraverso Format-Table:
LocalAddress LocalPort State OwningProcess
------------ --------- ----- -------------
127.0.0.1 8000 Listen 45608Sulla tua macchina l'ID di processo sarà diverso. Esegui Get-Process -Id <OwningProcess> con il tuo numero per confermare che il proprietario è il processo Python che hai avviato. Nella mia esecuzione era python.
Cosa controllare:
LocalAddress è 127.0.0.1. Il socket accetta connessioni solo da questa macchina. Se avessi omesso --bind, qui ti aspetteresti di vedere 0.0.0.0. Non ho testato questa variante sulla mia macchina, quindi prendilo come risultato atteso e non osservato.State è Listen. Il socket è in attesa di connessioni.OwningProcess indica il programma che ti aspetti. Se indica qualcosa che non riconosci, fermati e scopri di cosa si tratta prima di andare avanti.Apri un secondo terminale ed esegui:
curl -v http://127.0.0.1:8000/Questo è l'output della mia esecuzione, ridotto alle righe che contano:
* Trying 127.0.0.1:8000...
* Connected to 127.0.0.1 (127.0.0.1) port 8000
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.12.10
< Content-type: text/html
<!doctype html><title>lab</title><h1>Port 8000 lab</h1>Leggilo in ordine. Trying significa che curl sta aprendo una connessione TCP. L'indirizzo è già un IP, quindi non avviene alcuna risoluzione DNS. Connected significa che il three-way handshake è terminato. GET / HTTP/1.1 è il livello applicazione: ora curl parla HTTP su quella connessione. 200 OK significa che il server ha trovato il file e ha risposto. La riga HTML in fondo è il tuo file.
Il server scrive una riga sulla richiesta nel proprio terminale. Il mio ha mostrato:
127.0.0.1 - - [08/Nov/2026 06:07:56] "GET / HTTP/1.1" 200 -L'indirizzo di origine è lo stesso indirizzo di loopback, quindi la richiesta è partita da questa macchina.
Torna al terminale del server e premi Ctrl+C. È il modo pulito per fermarlo. Sulla mia macchina ho fermato il processo con Stop-Process -Force, che lo termina senza dargli modo di chiudersi in modo pulito. In entrambi i casi, il socket in ascolto sparisce quando il processo termina.
Conferma che il socket non c'è più:
Get-NetTCPConnection -State Listen -LocalPort 8000Il mio output era vuoto. Poi esegui di nuovo lo stesso comando curl:
curl -v http://127.0.0.1:8000/Questo è l'output della mia esecuzione:
* Failed to connect to 127.0.0.1 port 8000 after 2048 ms: Could not connect to server
curl: (7) Failed to connect to 127.0.0.1 port 8000 after 2048 ms: Could not connect to serverIl codice di uscita di curl è stato 7, il codice che indica che non è riuscito a collegarsi.
È successo questo. Il tuo SYN ha raggiunto 127.0.0.1 sulla porta 8000. Lì non c'era nessuno in ascolto, quindi il sistema operativo ha risposto con un reset. curl ha segnalato subito il rifiuto, senza aspettare un timeout. Se invece vedi una lunga attesa seguita da un timeout, sospetta un firewall o una route che scarta i pacchetti.
Lo stesso indirizzo e la stessa porta si sono comportati in due modi. Con un listener, l'handshake è stato completato e il server ha risposto. Senza listener, il rifiuto è tornato in millisecondi. A separare i due casi è l'elenco delle porte in ascolto. Quando una connessione ti sorprende, controlla prima quell'elenco.
Associarsi per sbaglio a tutte le interfacce. Molti server improvvisati ascoltano di default su tutti gli indirizzi. Su un portatile collegato alla rete di un bar, questo può esporre un server di sviluppo agli altri dispositivi sulla stessa rete. Usa un indirizzo di bind esplicito quando ti serve solo l'accesso locale.
Trattare il numero di porta come una prova d'identità. La porta 80 significa di solito HTTP, ma niente obbliga un programma a parlare HTTP lì. Collegati e leggi cosa invia il servizio, oppure controlla quale processo possiede il socket.
Dimenticare UDP. ss -tlnp mostra solo i listener TCP. Usa ss -ulnp per UDP, o Get-NetUDPEndpoint su Windows. Una macchina può avere un servizio UDP molto attivo che non compare mai nel tuo controllo TCP.
Leggere un timeout come "nessun servizio". Un timeout significa spesso che tra te e la porta c'è un filtro. Un rifiuto significa che l'host ha risposto e nessuno è in ascolto. Richiedono passi successivi diversi.
Terminare il processo sbagliato. Conferma il nome del processo proprietario e la sua riga di comando prima di fermarlo. Un server rimasto da un tentativo precedente può ancora occupare la porta e rispondere con i file di una cartella che non volevi pubblicare. Controlla il proprietario prima di dare per scontato che la porta sia tua.
Le porte in ascolto descrivono cosa la tua macchina offre alla sua rete, che tu lo volessi o no. Prima di testare qualsiasi altra cosa su un host tuo, elenca cosa è in ascolto, associa ogni voce a un programma che conosci e chiudi quello che non ti serve. Un server di sviluppo che hai dimenticato di fermare è un buon esempio di voce che sembra innocua e continua a rispondere.
Gli stessi comandi si possono usare su macchine che non controlli, ma scansionare o sondare un sistema che non è tuo richiede il permesso scritto del proprietario. Esercitati solo sulle tue macchine e nel tuo laboratorio.
Ripeti l'esercizio con UDP. Nella mia esecuzione un breve listener Python su 127.0.0.1, porta 5399, ha ricevuto un datagramma da un mittente. Il listener non faceva nulla di speciale:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("127.0.0.1", 5399))
data, addr = s.recvfrom(1024)
print("got", data, "from", addr)Eseguilo in un terminale, poi invia un datagramma da un altro con socket.sendto. L'output che ho ottenuto è stato got b'hello' from ('127.0.0.1', 63411). Nota che non c'è stato alcun handshake. Il pacchetto è arrivato e basta. Sulla mia macchina la porta 5353 era già in uso e il bind è fallito con un errore di permessi, quindi per il test ho scelto la 5399.
Poi leggi la parte sull'instaurazione della connessione nella RFC 9293 e confrontala con il flusso di pacchetti descritto sopra. Dopo passa a come funziona HTTP, che spiega come HTTP gira sopra TCP e cosa contengono una richiesta e una risposta.
RFC 9293, Transmission Control Protocol (TCP), IETF, agosto 2022. https://www.rfc-editor.org/rfc/rfc9293
RFC 768, User Datagram Protocol, IETF, agosto 1980. https://www.rfc-editor.org/rfc/rfc768
Service Name and Transport Protocol Port Number Registry, IANA. https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
RFC 6335, Internet Assigned Numbers Authority (IANA) Procedures for the Management of the Service Name and Transport Protocol Port Number Registry, IETF, agosto 2011. https://www.rfc-editor.org/rfc/rfc6335
Reti e web
Leggi uno scambio HTTP reale riga per riga: metodi, URL, header, classi di codici di stato e cookie, provati con curl su un server locale.
Reti e web
TLS 1.3 dà a HTTPS riservatezza, integrità e autenticazione del server. Impara l'handshake, le catene di certificati e ciò che il lucchetto non dimostra.
Reti e web
Come il DNS trasforma i nomi in indirizzi: resolver, server autoritativi, cache e TTL, i record A, AAAA, CNAME, MX e TXT e una query sicura da provare.