Un handshake che termina con un errore sembra un attacco finché non si guarda il messaggio, e il più delle volte è aritmetica: nessuna proposta condivisa, un certificato scaduto o un pacchetto che non è mai arrivato. Un attacco di downgrade è una cosa precisa: un avversario che spinge entrambe le estremità verso un comportamento più debole di quello che avrebbero scelto, e lascia prove diverse. Trattare ogni fallimento come malizia ti manda a cercare una vittima che forse non esiste, mentre trattare un vero downgrade come un bug di configurazione lascia il risultato più debole al suo posto. I due errori costano cose diverse, e solo uno è economico da correggere.

Cosa cambia davvero un attacco di downgrade

L'obiettivo non è rompere la cifratura né indovinare una chiave. È influenzare la negoziazione perché entrambe le parti si fermino su una versione più vecchia o un algoritmo più debole che l'attaccante può gestire, o far credere a una parte che l'altra supporti solo quello.

Ciò che cambia è l'accordo, non l'autenticazione. Se l'attaccante può alterare l'offerta senza essere rilevato, i peer possono comunque completare un handshake e continuare ad autenticarsi, con parametri che l'attaccante preferisce. Per questo la domanda interessante è se la negoziazione stessa sia coperta dalla protezione dell'integrità.

Perché la negoziazione stessa deve essere autenticata

TLS 1.3 rende esplicita quella protezione. Un client che ha offerto la versione più recente può rilevare un server che è tornato indietro in silenzio, perché il valore casuale del server porta un pattern fisso quando negozia 1.2 o inferiore, e il client deve interrompere vedendolo. Anche l'intero transcript è coperto dai messaggi finali, quindi una modifica ovunque nella negoziazione fallisce più tardi invece di passare in silenzio.

Le versioni più vecchie del protocollo avevano storie più deboli, ed è proprio per questo che contano le regole di fallback. Un endpoint TLS 1.0 nella stessa configurazione non è un attacco di downgrade; è un esito consentito che hai scelto di permettere. La distinzione è se il risultato più debole sia venuto da una decisione protetta o da un percorso non protetto su cui un intermediario poteva influire. Una volta impostata così, la maggior parte degli incidenti si riduce a una questione di configurazione.

IKEv2 si comporta diversamente ma poggia sulla stessa idea. La proposta viaggia in uno scambio che poi autentica entrambi i peer, quindi un middlebox che riscrive gli algoritmi rompe l'autenticazione invece di guidarla. Il canale di controllo di OpenVPN protegge la negoziazione con TLS e poi usa quel canale per concordare i cifrari del canale dati, ed è per questo che la riga di log del canale di controllo è quella interessante.

La compatibilità configurata non è un attacco

Un amministratore può permettere deliberatamente scelte più vecchie per supportare un client documentato, e quella decisione è visibile nella configurazione. Un attaccante non può creare quella configurazione; può solo sfruttare un percorso che gli consente di influire sull'offerta.

La riparazione pericolosa è quella che rimuove la prova della differenza. Abilitare ogni opzione legacy, disattivare la verifica dei certificati o accettare qualsiasi cifrario per far sparire un errore amplia ciò che l'endpoint accetterà, ed elimina i controlli che avrebbero segnalato il risultato più debole.

Se devi mantenere un'opzione vecchia, indica il motivo e il client nel commento di configurazione, poi limita l'opzione al profilo che ne ha bisogno. Un'eccezione di compatibilità limitata a un solo listener è verificabile; una applicata globalmente è solo un valore predefinito più debole che nessuno ricorda di aver scelto.

Leggi il fallimento prima di dargli un nome

Conserva la stringa di errore esatta, le versioni di client e server e l'ora prima di qualsiasi modifica. Poi classifica: i peer non erano d'accordo su una proposta, il percorso dei pacchetti ha fallito, o una parte ha rifiutato un parametro che un tempo accettava?

Un disallineamento di proposta di solito nomina le versioni o gli algoritmi colpevoli e fallisce nello stesso punto ogni volta. Un problema di routing o MTU si blocca o va in timeout, e spesso varia con la rete. Un certificato scaduto produce un errore di verifica, non di negoziazione.

Niente di tutto questo è prova di un attaccante. Assegnare una causa malevola al primo fallimento è l'errore, perché nasconde il bug deterministico che fallirà di nuovo domani.

Conferma cosa è stato realmente selezionato

Dopo qualsiasi correzione, leggi il risultato negoziato invece di fidarti che la connessione sia salita. Una connessione che riesce perché la verifica è stata rimossa è il risultato che volevi evitare. I due comandi che seguono mostrano la versione e il cifrario scelti per un endpoint con TLS davanti, più la riga del canale di controllo dal log di un server OpenVPN.

openssl s_client -connect 198.51.100.30:443 -servername vpn.example.net -brief </dev/null
grep -iE 'Control Channel|Data Channel' /var/log/openvpn.log | tail -5

Confronta il risultato con la configurazione che intendevi, non con ciò che ora si connette. Se la versione selezionata è più vecchia del minimo consentito, il profilo o la lista di fallback è dove guardare, e la risposta è correggere quella lista invece di dichiarare un attacco e ricostruire tutto intorno.

Articoli correlati