Il bug Heartbleed è stato prevenuto se OpenSSL è stato scritto in Go / D / Vala?

18

La vulnerabilità Heartbleed si verifica a causa di un bug nel codice sorgente C di OpenSSL, eseguendo un memcpy() da un buffer che è troppo corto. Mi chiedo se il bug sarebbe stato automaticamente impedito in altri linguaggi con sistemi di gestione della memoria di livello superiore rispetto a C o C ++.

In particolare, la mia comprensione è che Vai , D e Vala ciascuno si compila in codice nativo, non ha bisogno di una VM per funzionare e dovrebbe consentire la scrittura di librerie native che forniscono una C interfaccia binaria compatibile.

Quindi, questi linguaggi potrebbero essere utilizzati per implementare la libreria OpenSSL con la stessa interfaccia e offrire protezione contro bug come la vulnerabilità Heartbleed?

    
posta oliver 08.04.2014 - 21:27
fonte

5 risposte

20

In realtà nessuna di queste lingue avrebbe prevenuto il bug, ma avrebbero ridotto le conseguenze.

Il codice di OpenSSL sta facendo qualcosa che, dal punto di vista della macchina astratta, è privo di senso: legge più byte da un buffer di quanti ce ne siano effettivamente in un buffer. Con C, la lettura continua a "funzionare" e restituisce i byte rimanenti dopo il buffer. Con linguaggi più rigorosi, l'accesso alla memoria fuori dai limiti sarebbe stato intrappolato e innescato un'eccezione: invece di leggere e inviare i byte, il codice incriminato si sarebbe solo arrestato, portando (nel contesto di un server Web) alla chiusura di il thread corrente e probabilmente la chiusura della connessione, senza alterare il resto del server.

Quindi, ancora un bug, ma non è più una vera vulnerabilità.

Questo ha solo una relazione indiretta con la gestione automatica della memoria. La vera rete di sicurezza qui è il controllo sistematico degli array sugli accessi. Tale verifica sistematica è quindi supportata indirettamente dalla tipizzazione rigorosa (che impedisce l'utilizzo di nient'altro che una "matrice di byte" come una "matrice di byte"). I tipi rigorosi sono a loro volta supportati indirettamente dalla gestione automatica della memoria (il GC), in quanto prevengono i puntatori penzolanti e quindi le condizioni use-after-free che violano la tipizzazione rigorosa.

Il recente "heartbleed" non è una cosa qualitativamente nuova; OpenSSL ha già avuto parecchi bug relativi all'overflow del buffer nel corso degli anni (alcuni dei quali provenienti dal codice di gestione ASN.1, per l'analisi dei certificati). Questo è solo un altro alla lista.

    
risposta data 08.04.2014 - 23:49
fonte
17

Se consideri il bug come una lettura fuori dai limiti della struttura attuale, probabilmente questo sarebbe stato prevenuto in altre lingue, perché non si ha accesso illimitato alla memoria e sarebbe necessario implementare queste cose in modo diverso.

Ma preferirei classificare questo bug come mancata convalida dell'input dell'utente, ad es. ritiene che la dimensione inviata nel pacchetto sia effettivamente la dimensione del carico utile. Questo tipo di bug non viene semplicemente corretto usando un'altra lingua.

    
risposta data 08.04.2014 - 23:37
fonte
9

Sfortunatamente, il bug non è stato prevenuto, perché OpenSSL usa il proprio allocatore di memoria , piuttosto che quello fornito dal sistema.

Il buffer da cui vengono letti i infami dati heartbeat viene allocato da una funzione chiamata freelist_extract in SSL / s3_both.c . Questa funzione, per impostazione predefinita, gestisce la propria lista di memoria utilizzata / non utilizzata di OpenSSL e non esegue nessuno dei moderni controlli di sicurezza.

Anche se fosse stato scritto in un'altra lingua, supponendo che OpenSSL avesse ancora mantenuto il proprio allocatore di buffer, allora questo errore sarebbe successo lo stesso. Riutilizzando una precedente struttura di buffer, indipendentemente dal linguaggio di programmazione, la funzione memcpy o "buffer copy" equivalente avrebbe fatto la stessa cosa senza generare errori.

In un linguaggio di programmazione moderno, sarebbe qualcosa di simile a:

request = last_used_buffer;
/* I'm sure it doesn't actually read bytes like this, but you get the idea */
while (byte = read(connection)) {
    request[i++] = byte;
}

/* ... some time later, in the heartbeat processing function */

output = new Buffer();
output.write(header);
output.write(request, start, len); /* dutifully copies from the request buffer,
                                      but since end was not checked, it can copy
                                      bytes from last_used_buffer */

Se invece OpenSSL utilizza direttamente il sistema (libc) malloc e free piuttosto che il proprio allocatore, questo errore potrebbe essere stato rilevato un paio di anni fa. Molte implementazioni di libc forniscono controlli dei limiti molto migliori sulla memoria allocata / liberata e strumenti come valgrind potrebbero aver raccolto facilmente questo bug.

Questa conseguenza dell'amplificatore di memoria di OpenSSL nel funzionamento del bug heartbleed è stata menzionata da Ted Unangst all'indirizzo: link

    
risposta data 10.04.2014 - 10:31
fonte
7

Penso di poter rispondere a questa domanda per il caso specifico di una libreria crittografica scritta in Go --- è facile, e non del tutto ipotetica, perché c'è già un Pacchetto TLS, crypto/tls in Go che non dipende da nessuna libreria esterna.

Nonostante il tipico overflow del buffer, il Go idiomatico è molto più sicuro del tradizionale C, Go offre allo sviluppatore appassionato molte opzioni per aggirarlo, come imitare l'aritmetica del puntatore di C tramite Go unsafe.pointer . Ci si chiede se si possa essere d'accordo a non usare il codice fragile in un software critico.

La crittografia, ovviamente, è esattamente il tipo di software che utilizza un codice così fragile, per buone ragioni. Dopo tutto, i confronti a tempo costante implementati nel pacchetto Go crypto.subtle effettivamente richiedono e hanno, allo stesso modo, terribili avvertimenti sull'uso accurato come quelli di unsafe . L'unica domanda rimanente, in realtà, è se qualche bug può ancora sopravvivere in un tale ambiente.

Per quanto ne so, Go implementa in effetti i confronti a tempo costante dei valori di hash correttamente. Non mi sono preoccupato nemmeno di guardare se gli hash complicati che coinvolgono gli S-box sono calcolati in un tempo costante --- nessuno si è preoccupato di inserirli in un pacchetto con nomi come subtle e avvertimenti su come sia facile rompere le cose , quindi sono davvero dubbioso.

Quello che ho controllato è che la crittografia a curva ellittica è non implementata in modo costante in Go. Nessuno sembra nemmeno aver considerato il minimo tentativo - l'implementazione chiama molte funzioni integer di lunghezza arbitraria non progettate nemmeno per l'uso crittografico, e in effetti utilizzando algoritmi non-costanti. Quando questo tipo di canale temporale è stato mostrato di recente anche in OpenSSL su un'architettura, è stato abbastanza buono per a carta che dimostra compromissione della chiave privata .

La stessa situazione continua esiste in Go, su tutte le architetture. E, beh, non sembra davvero che a nessuno importi questo tipo di dettagli come la criptazione realmente funzionante; l'attenzione è solo sulla leggibilità e velocità (beh, e lavorando nel modo in cui l'utente casuale e alcuni test di unità sono ingannati da esso, immagino). Dopo tutto, perché dovresti preoccuparti di scegliere un algoritmo adatto quando la lingua ti protegge già dalla maggior parte dei modi di introdurre buffer overflow e quando scegliere un algoritmo corretto rischia di rovinare l'affermazione di Google che TLS è diventato a buon mercato dal punto di vista computazionale? Sarcasmo a parte, rendere la lingua responsabile per la cattura dei nostri bug significa solo che tutti quegli errori che la lingua non può catturare per noi saranno ancora lì.

Infine, le librerie come OpenSSL hanno i vantaggi che correzioni di bug tempestive sono una possibilità ragionevole. Anche se in linea di principio lo stesso vale per i pacchetti Go, una volta che qualcosa diventa parte del nucleo di Go, come il pacchetto TLS, viene influenzato dal programma di rilascio. Ovviamente ciò può interferire con l'implementazione tempestiva di correzioni di bug. Suppongo che Go-1.3, a causa di questa estate, non risolverà certamente il problema di temporizzazione di ECC anche se fosse riconosciuto come il problema critico, dato il blocco delle funzionalità.

    
risposta data 08.04.2014 - 23:55
fonte
3

La contaminazione dei dati è stata implementata in Netscape JavaScript (navigator 3 e sul server in Enterprise Server) in risposta a realizzazioni abbastanza precoci sulla natura della sicurezza su Internet. Tutti gli input provenienti dall'utente sono stati considerati contaminati, a meno che il flag non sia stato cancellato e il flag di taint si diffonda tramite operazioni sui dati (quindi il risultato della combinazione di dati e dati contaminati è considerato contaminato). Di conseguenza, i dati contaminati potrebbero sempre essere controllati.

Questo era più di 10 anni fa . Mi fa schifo che questo non ha trovato la sua strada nei linguaggi gestiti mainstream come Java o C # (o l'implementazione JavaScript di chiunque altro).

Se avessi combinato l'analisi dell'inquinamento dei dati in fase di compilazione con un modello di memoria sicura (codice gestito, o almeno verificabile), avresti comunque dei bug logici, ma avresti eliminato intere categorie di attacchi in un colpo, includendo entrambi i fattori che contribuiscono a questo.

    
risposta data 10.04.2014 - 16:23
fonte

Leggi altre domande sui tag