Sarebbe buona pratica di programmazione sicura sovrascrivere una variabile "sensibile" prima di eliminarla?

77

È buona prassi di programmazione sicura sovrascrivere i dati sensibili memorizzati in una variabile prima che vengano cancellati (o che non rientrino nell'ambito di applicazione)? Il mio pensiero è che impedirebbe a un hacker di essere in grado di leggere i dati latenti nella RAM a causa della permanenza dei dati. Ci sarebbe una maggiore sicurezza nel sovrascriverlo più volte? Ecco un piccolo esempio di ciò di cui sto parlando, in C ++ (con commenti inclusi).

void doSecret()
{
  // The secret you want to protect (probably best not to have it hardcoded like this)
  int mySecret = 12345;

  // Do whatever you do with the number
  ...

  // **Clear out the memory of mySecret by writing over it**
  mySecret = 111111;
  mySecret = 0;
  // Maybe repeat a few times in a loop
}

Un pensiero è, se questo aggiunge effettivamente sicurezza, sarebbe bello se il compilatore aggiungesse automaticamente le istruzioni per farlo (forse di default, o forse dicendo al compilatore di farlo quando cancella le variabili).

This question was featured as an Information Security Question of the Week.
Read the Dec 12 2014 blog entry for more details or submit your own Question of the Week.

    
posta Jonathan 04.12.2014 - 17:18
fonte

10 risposte

71

Sì, è una buona idea sovrascrivere, quindi eliminare / rilasciare il valore. Non dare per scontato che tutto ciò che devi fare è "sovrascrivere i dati" o lasciarlo fuori dal campo di applicazione del GC, perché ogni lingua interagisce con l'hardware in modo diverso.

Durante la protezione di una variabile potresti dover pensare a:

  • crittografia (in caso di dump della memoria o di memorizzazione nella cache delle pagine)
  • inserimento nella memoria
  • possibilità di contrassegnare come di sola lettura (per evitare ulteriori modifiche)
  • costruzione sicura NON permettendo che una stringa costante venga passata
  • ottimizzazione dei compilatori (vedere nota nell'articolo collegato re: macro ZeroMemory)

L'effettiva implementazione di "cancellazione" dipende dalla lingua e dalla piattaforma. Cerca la lingua che stai utilizzando e verifica se è possibile codificare in modo sicuro.

Perché questa è una buona idea? Crashdump e tutto ciò che contiene l'heap potrebbe contenere i tuoi dati sensibili. Prendi in considerazione la possibilità di utilizzare quanto segue per proteggere i tuoi dati in memoria

Fai riferimento a StackOverflow per le guide all'implementazione per lingua.

Dovresti essere consapevole del fatto che anche quando si utilizza la guida del fornitore (MSFT in questo caso) è ancora possibile dump del contenuto di SecureString e potrebbe avere specifiche linee guida sull'utilizzo per scenari di elevata sicurezza.

    
risposta data 04.12.2014 - 17:26
fonte
34

Memorizzare un valore che non viene utilizzato di nuovo? Sembra qualcosa che sarebbe stato ottimizzato, indipendentemente dai benefici che potrebbe fornire.

Inoltre, potresti non sovrascrivere i dati nella memoria a seconda di come funziona la lingua stessa. Ad esempio, in una lingua che utilizza un garbage collector, non verrebbe rimosso immediatamente (e questo presuppone che non ci siano altri riferimenti in sospeso).

Ad esempio, in C #, penso che quanto segue non funzioni.

string secret = "my secret data";

...lots of work...

string secret = "blahblahblah";

"my secret data" si blocca fino a quando non viene raccolta la garbage perché è immutabile. Quest'ultima linea in realtà sta creando una nuova stringa e sta avendo un punto segreto. Non velocizza la velocità di rimozione dei dati segreti effettivi.

C'è un vantaggio? Supponendo che lo scriviamo in assembly o in un linguaggio a bassa leva, così possiamo assicurarci di sovrascrivere i dati, mettere il nostro computer in stop o lasciarlo acceso con l'applicazione in esecuzione, e abbiamo la nostra RAM raschiata da una cameriera cattiva, e la cameriera cattiva ha ottenuto i nostri dati RAM dopo che il segreto è stato sovrascritto ma prima sarebbe stato appena cancellato (probabilmente uno spazio molto piccolo), e nient'altro nella RAM o sul disco rigido avrebbe dato via questo segreto ... quindi vedo un possibile aumento in sicurezza.

Tuttavia, il costo rispetto al vantaggio sembra rendere questa ottimizzazione della sicurezza molto bassa sul nostro elenco di ottimizzazioni (e al di sotto del punto di "utile" per la maggior parte delle applicazioni in generale).

Potrei forse vedere un uso limitato di questo in speciali chip intesi a contenere segreti per un breve periodo di tempo per garantirne il mantenimento nel più breve tempo possibile, ma anche allora non sono sicuro di alcun vantaggio per i costi.

    
risposta data 04.12.2014 - 17:45
fonte
17

È necessario un modello di minaccia

Non dovresti nemmeno cominciare a pensare di sovrascrivere le variabili di sicurezza finché non hai un modello di minaccia che descrive quali tipi di hack stai cercando di prevenire. La sicurezza ha sempre un costo. In questo caso, il costo è il costo di sviluppo degli insegnanti che insegnano a mantenere tutto questo codice aggiuntivo per proteggere i dati. Questo costo significa che potrebbe essere più probabile che i tuoi sviluppatori commettano errori e che questi errori siano più probabili la causa di una perdita rispetto a un problema di memoria.

  • L'attaccante può accedere alla tua memoria? Se è così, c'è una ragione per cui pensi che non potrebbero / non solo annusano il valore prima che lo sovrascrivi? Che tipo di tempo ha l'hacker per accedere alla tua memoria
  • L'attaccante può accedere ai core dump? Ti dispiace se possono accedere ai dati sensibili in cambio di essere abbastanza rumorosi da causare un core dump in primo luogo?
  • Questo è open source o closed source? Se è open source, devi preoccuparti di più compilatori, perché i compilatori ottimizzano sempre cose come i dati sovrascritti. Il loro compito non è quello di fornire sicurezza. ( Per un esempio di vita reale, PasswordSafe di Schneier dispone di classi specializzate per proteggere i dati della password non crittografati. Per farlo, utilizza le funzioni API di Windows per bloccare la memoria e forzarla a essere sovrascritta correttamente piuttosto che usare il compilatore per farlo per lui )
  • È un linguaggio raccolto? Sai come forzare la tua particolare versione del tuo particolare garbage collector a sbarazzarti dei tuoi dati?
  • Quanti tentativi può fare l'attaccante per ottenere dati sensibili prima che tu lo noti e interromperlo con altri mezzi (come i firewall)?
  • Viene eseguito su una macchina virtuale? Sei sicuro della sicurezza del tuo Hypervisor?
  • Un utente malintenzionato ha accesso fisico? Ad esempio, Windows è più che felice di utilizzare un'unità flash per memorizzare nella cache la memoria virtuale. Tutto ciò che un utente malintenzionato dovrebbe fare è convincere Windows a spingerlo sul flash drive. Quando ciò accade, è DAVVERO difficile farlo uscire. È così difficile, infatti, che non esiste un metodo riconosciuto per la cancellazione affidabile dei dati da un'unità flash.

Queste domande devono essere affrontate prima di considerare di provare a sovrascrivere i dati sensibili. Il tentativo di sovrascrivere i dati senza affrontare il modello di thread è un falso senso di sicurezza.

    
risposta data 06.12.2014 - 17:45
fonte
14

Sì, è buona norma per la sicurezza sovrascrivere i dati particolarmente sensibili quando i dati non sono più necessari, cioè come parte di un distruttore di oggetti (un distruttore esplicito fornito dal linguaggio o un'azione che il programma richiede prima di rilasciare l'oggetto). È addirittura buona prassi sovrascrivere i dati che non sono di per sé sensibili, ad esempio per azzerare i campi del puntatore in una struttura dati che non è più utilizzabile e anche azzerare i puntatori quando l'oggetto a cui puntano viene liberato anche se si conosce non utilizzerai più quel campo.

Un motivo per farlo è nel caso in cui i dati passino attraverso fattori esterni come un core dump esposto, un'immagine di ibernazione rubata, un server compromesso che consente un dump di memoria dei processi in esecuzione, ecc. Attacchi fisici in cui un utente malintenzionato estrae la RAM bastoni e fa uso di dati di rimanenza sono raramente una preoccupazione tranne che su computer portatili e forse dispositivi mobili come telefoni (dove la barra è più alta perché la RAM è saldata), e anche allora solo in scenari mirati solo. La rimanenza dei valori sovrascritti non è un problema: occorrerebbe un hardware molto costoso per sondare all'interno di un chip RAM per rilevare eventuali differenze di tensione microscopiche persistenti che potrebbero essere influenzate da un valore sovrascritto. Se sei preoccupato per gli attacchi fisici alla RAM, una preoccupazione più grande sarebbe quella di assicurare che i dati siano scritti nella RAM e non solo nella cache della CPU. Ma, di nuovo, di solito è una preoccupazione molto minore.

Il motivo più importante per sovrascrivere i dati obsoleti è come difesa dai bug del programma che causano l'uso di memoria non inizializzata, come il famigerato heartbleed. Questo va al di là dei dati sensibili perché il rischio non è limitato a una perdita di dati: se c'è un bug software che causa il dereferenziamento di un campo puntatore senza essere stato inizializzato, il bug è meno incline allo sfruttamento e più facile da tracciare se il campo contiene tutto-bit-zero rispetto a se potenzialmente punta a una posizione di memoria valida ma priva di significato.

Fai attenzione ai buoni compilatori che ottimizzeranno l'azzeramento se rileveranno che il valore non è più utilizzato. Potrebbe essere necessario utilizzare qualche trucco specifico del compilatore per far sì che il compilatore creda che il valore rimanga in uso e quindi generi il codice di azzeramento.

In molte lingue con gestione automatica, gli oggetti possono essere spostati in memoria senza preavviso. Ciò significa che è difficile controllare le perdite di dati obsoleti, a meno che il gestore della memoria stesso non cancelli la memoria inutilizzata (spesso non lo fanno, per le prestazioni). Tra l'altro, le fughe esterne sono di solito tutto ciò di cui ti devi preoccupare, poiché i linguaggi di alto livello tendono a precludere l'uso della memoria non inizializzata (fai attenzione alle funzioni di creazione di stringhe nelle lingue con stringhe mutabili).

A proposito, ho scritto "zero out" sopra. È possibile utilizzare un modello di bit diverso da tutti gli zeri; tutti gli zeri ha il vantaggio che è un puntatore non valido nella maggior parte degli ambienti. Un po 'di schema che conosci è un puntatore non valido ma che è più distintivo può essere utile nel debug.

Molti standard di sicurezza impongono la cancellazione di dati sensibili come le chiavi. Ad esempio, lo standard FIPS 140-2 per i moduli crittografici lo richiede anche al livello di garanzia più basso, che a parte ciò richiede solo conformità funzionale e non resistenza agli attacchi.

    
risposta data 04.12.2014 - 19:02
fonte
8

Per un'aggiunta (si spera interessante) al resto delle risposte, molte persone sottovalutano la difficoltà nel sovrascrivere correttamente la memoria con C. Sto andando a citare pesantemente dal post del blog di Colin Percival intitolato " Come azzerare un buffer ".

Il principale problema dei tentativi ingenui di sovrascrivere la memoria in C è l'ottimizzazione del compilatore. I compilatori più moderni sono abbastanza "intelligenti" da riconoscere che gli idoni comuni utilizzati per sovrascrivere la memoria non modificano di fatto il comportamento osservabile del programma e possono essere ottimizzati. Sfortunatamente questo rompe completamente ciò che vogliamo raggiungere. Alcuni dei trucchi comuni sono descritti nel post del blog linkato sopra. Peggio ancora, un trucco che funziona per una versione di un compilatore potrebbe non funzionare necessariamente con un altro compilatore o anche con una versione diversa dello stesso compilatore. A meno che non stiate distribuendo i binari solo , questa è una situazione problematica.

L'unico modo per sovrascrivere in modo affidabile la memoria in C che io conosca è memset_s funzione. Purtroppo, questo è disponibile solo in C11, quindi i programmi scritti per versioni precedenti di C sono sfortunati.

The memset_s function copies the value of c (converted to an unsigned char) into each of the first n characters of the object pointed to by s. Unlike memset, any call to memset_s shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.3. That is, any call to memset_s shall assume that the memory indicated by s and n may be accessible in the future and therefore must contain the values indicated by c.

Indubbiamente, Colin Percival ritiene che sovrascrivere la memoria non sia abbastanza. Da un post sul blog di follow up intitolato " L'azzeramento dei buffer è insufficiente " , afferma

With a bit of care and a cooperative compiler, we can zero a buffer — but that's not what we need. What we need to do is zero every location where sensitive data might be stored. Remember, the whole reason we had sensitive information in memory in the first place was so that we could use it; and that usage almost certainly resulted in sensitive data being copied onto the stack and into registers.

Continua e fornisce l'esempio delle implementazioni AES utilizzando il set di istruzioni AESNI su piattaforme x86 che perdono dati nei registri.

Afferma che,

It is impossible to safely implement any cryptosystem providing forward secrecy in C.

Un'affermazione preoccupante.

    
risposta data 16.12.2014 - 13:48
fonte
4

È importante sovrascrivere i dati sensibili immediatamente dopo che è necessario perché, altrimenti:

  • I dati rimangono nello stack fino a quando non vengono sovrascritti e potrebbero essere visualizzati con un overflow del frame da una procedura diversa.
  • I dati sono soggetti allo scraping della memoria.

In effetti, se osservi il codice sorgente per le applicazioni sensibili alla sicurezza (ad esempio openssh ), troverai che elimina accuratamente i dati sensibili dopo l'uso.

È anche vero che i compilatori potrebbero cercare di ottimizzare la sovrascrittura e, anche se non è così, è importante sapere come vengono archiviati i dati fisicamente (ad esempio se il segreto è memorizzato su SSD, sovrascrivendolo potrebbe non cancellare vecchio contenuto a causa di livellamento dell'usura ).

    
risposta data 05.12.2014 - 04:37
fonte
3

L'esempio originale mostra una variabile stack perché è un tipo nativo int.

La sovrascrittura è una buona idea, altrimenti rimane in pila fino a quando non viene sovrascritta da qualcos'altro.

Sospetto che se sei in C ++ con oggetti heap o tipi nativi C assegnati tramite puntatori e malloc che sarebbe una buona idea

  1. Usa volatile
  2. Utilizza la direttiva per circondare il codice usando quella variabile e disabilita le ottimizzazioni.
  3. Se possibile, assembla il solo segreto in valori intermedi piuttosto che variabili nominate, quindi esiste solo durante i calcoli.

Sotto JVM o C #, penso che tutte le scommesse siano scadute.

    
risposta data 07.12.2014 - 06:49
fonte
2

È possibile che il blocco della memoria sia stato eliminato prima della tua partenza e cancellato, a seconda di come la distribuzione dell'utilizzo nel file di paging riproduce i dati che potrebbero vivere lì per sempre .

Quindi per rimuovere con successo il segreto dal computer devi prima assicurarti che i dati non raggiungano mai la memoria persistente con appuntamenti le pagine. Quindi assicurati che il compilatore non ottimizzi le scritture nella memoria da cancellare.

    
risposta data 05.12.2014 - 13:00
fonte
1

In realtà la risposta è sì, probabilmente dovresti sovrascriverlo prima di eliminarlo. Se sei uno sviluppatore di applicazioni web, dovresti probabilmente sovrascrivere tutti i dati prima di utilizzare le funzioni delete o free .

Esempio di come sfruttare questo bug:

L'utente può inserire alcuni dati dannosi nel campo di input. Si procede con i dati e quindi si chiama la funzione free della memoria allocata senza sovrascriverla, quindi i dati rimarranno nella memoria. Quindi l'utente fa cadere la tua applicazione web (ad esempio caricando un'immagine molto grande o qualcosa del genere) e il sistema UNIX salverà il dump della memoria di base in core o core.<pid> file. Quindi se l'utente può eseguire il brute su <pid> , il che non richiederà troppo tempo e il file di dump di base verrà interpretato come una shell Web, poiché contiene dati dannosi dell'utente.

    
risposta data 17.12.2014 - 08:20
fonte
-2

Non int secret = 13123 una costante in ambito locale?

Non dovresti essere troppo preso da ciò che stai scrivendo è ovunque che valga la pena di essere letto. In effetti, suggerirei, con un po 'di consiglio controcorrente, di lasciarlo deliberatamente leggibile. E inoltre, popolarlo a caso con stringhe che sono correlate nel tempo, che non vengono chiamate nel modo standard.

In questo modo, se controlli i siti web oscuri per scoprire se sei stato compromesso, puoi dire esattamente come è stato fatto. Poiché un dump dump è separato da un dump raschiatura.

    
risposta data 17.12.2014 - 08:52
fonte

Leggi altre domande sui tag