2010-09-24 14 views
37

Devo scrivere un servizio Windows che gestisca ad un certo punto dati riservati (come codici PIN, password e così via). Queste informazioni sono necessarie per un periodo di tempo molto breve: in genere vengono inviate quasi immediatamente a un lettore di smart card.Come fare in modo che le ottimizzazioni del compilatore non introducano un rischio per la sicurezza?

Consente di considerare questo pezzo di codice:

{ 
    std::string password = getPassword(); // Get the password from the user 

    writePasswordToSmartCard(password); 

    // Okay, here we don't need password anymore. 
    // We set it all to '\0' so it doesn't stay in memory. 
    std::fill(password.begin(), password.end(), '\0'); 
} 

Ora la mia preoccupazione è di circa ottimizzazioni del compilatore. Qui il compilatore potrebbe rilevare che la password sta per essere eliminata e che la modifica del suo valore a questo punto è inutile e basta rimuovere la chiamata.

Non mi aspetto che il mio compilatore si preoccupi del valore della memoria futura senza riferimento.

Le mie preoccupazioni sono legittime? Come posso essere sicuro che una tale parte di codice non verrà ottimizzata?

risposta

32

Sì, le tue preoccupazioni sono legittime. È necessario utilizzare funzioni appositamente progettate come SecureZeroMemory() per impedire alle ottimizzazioni di modificare il comportamento del codice.

Non dimenticare che la classe stringa deve essere stata progettata specificamente per la gestione delle password. Ad esempio, se la classe rialloca il buffer per contenere una stringa più lunga, deve cancellare il buffer prima di riconnetterlo all'allocatore di memoria. Non sono sicuro, ma è probabile che lo std::string non lo faccia (almeno per impostazione predefinita). L'utilizzo di una classe di gestione delle stringhe inadeguata rende tutte le tue preoccupazioni prive di valore: avrai la password copiata su tutta la memoria del programma, fino a quando non lo saprai.

+0

Grazie. Mi chiedo solo come funzioni questa funzione: cosa impedisce al compilatore di ottimizzarlo? – ereOn

+2

@ereOn: È facile, il suo codice non è presentato al compilatore quando il programma è compilato, quindi il compilatore non può vederlo e decide che "non fa nulla di utile". Ad esempio, può essere compilato in una DLL già e solo collegato dinamicamente a. – sharptooth

+0

@sharptooth: ha senso, anzi. Lo accetterò appena possibile;) Avete qualche link/tutorial sulla scrittura di una classe di gestione delle password sicura? – ereOn

9

È problematico, ma per un altro motivo. Chi ha detto che std::string password = getPassword(); non lascia ancora un'altra copia in memoria? (Probabilmente hai bisogno di scrivere una classe di allocatori "sicura" per questa memoria di zeri su "destruct" o "deallocate")

Nella tua tranquillità di codice puoi evitare l'ottimizzazione ottenendo un puntatore volatile ai dati di stringa (I non so se puoi farlo in modo standard) e quindi azzerare i dati.

+0

Bene, la cosa 'getPassword()' è stata appena creata per scrivere il mio codice di esempio. Ma hai assolutamente ragione, bisogna preoccuparsi anche di questo. – ereOn

+0

@ereOn In qualsiasi modo si generi la password, in questo caso si deve essere estremamente attenti. Il modo più semplice consiste nell'utilizzare un allocatore personalizzato che zeri ha deallocato la memoria con uno dei metodi precedenti (volatile o SecureZeroMemory). Si noti che std :: string non distrugge i suoi elementi, solo deallocati. – ybungalobill

-1

Dichiarare la password volatile per impedire al compilatore di formulare ipotesi sulla rimozione di letture o scritture esplicite.

volatile std::string password = getPassword(); // Get the password from the user 
+2

Potrei sbagliarmi ma credo che 'std :: string' non sia stato progettato per essere' volatile': tutti i suoi metodi non sono dichiarati 'volatile' quindi non potrò chiamarli. – ereOn

+1

@ereOn: Forse la soluzione più sicura è utilizzare una stringa C in cui si ha il controllo completo della memoria utilizzata per memorizzare la stringa. – Clifford

+0

Sì. Se alla fine, non c'è nulla che mi senta abbastanza a mio agio con, sicuramente finirò per usare una stringa C. – ereOn

0

Perché non disattivare l'ottimizzazione per il codice in questione?

#pragma optimize("", off) 

// Code, not to optimize goes here 

#pragma optimize("", on) 

Questo campione di #pragma optimize è specifico per MSVC, ma altri compilatori supportano pure.

1

In questo caso specifico, sarei davvero sorpreso se il compilatore può ottimizzare una chiamata al metodo che potrebbe chiaramente avere effetti collaterali. Oppure è std :: compilare in linea in modo che il compilatore possa vedere l'implementazione? (Non sono un programmatore C++).

Detto questo, questo genere di cose può essere una preoccupazione in generale. Ma devi pensare a quanto sia facile da sfruttare. Per leggere la memoria di un altro processo, un utente malintenzionato avrebbe bisogno di un certo livello di accesso come amministratore (in caso contrario, perché stai usando quel sistema operativo). Se la macchina è compromessa a quel livello, hai già perso.

+0

Sono tendenzialmente d'accordo. Tuttavia, al mio posto, quando un software si blocca, un file di dump viene inviato a un servizio specifico o allo stesso sviluppatore. Anche se ci si può fidare di queste persone, se posso evitare che i dati riservati vengano archiviati da qualche parte, è ancora meglio. – ereOn

+0

@ereOn: ho paura che il file di dump, se contiene l'immagine del processo, è di per sé un rischio per la sicurezza. Nulla di ciò che fai per cancellare i dati sensibili può proteggerti perché lo sviluppatore ha accesso al codice e può facilmente sovvertire le misure di sicurezza. – JeremyP

+0

Lo sviluppatore non può modificare il codice eseguito nel nostro ambiente di produzione. Se il codice è ben progettato e sicuro, sapere come funziona non può aiutare a hackerare i dati. E se riesci a rimuovere tutti i dati sensibili dalle discariche, anche quelli non possono aiutarti. – ereOn

7

Non uso std::string per le password, in quanto non zero fuori di essa la memoria quando si fa riassegnazioni o distruzione - progettare il proprio ConfidentialString classe, invece.Quando si progetta quella classe, si potrebbe voler approfittare di CryptProtectMemory ... e stare molto, molto attenti quando è necessario utilizzare la versione decrittografata, soprattutto quando si chiama il codice esterno.

+0

Non ero a conoscenza di tale funzione. Grazie, sarà sicuramente d'aiuto. – ereOn

+0

Non devi inventare la tua classe di stringhe da zero, solo il tuo [allocatore] (http://stackoverflow.com/questions/3785366/how-to-ensure-that-compiler-optimizations-dont-introduce- a-rischio sicurezza/3785401 # 3785401). –

+1

@ Poger patè: probabilmente stai ancora meglio girando il tuo - rende più facile controllare quali API esterne puoi (e non puoi!) Passare la password a, ti consente di mantenere la stringa crittografata internamente ed evitare la copia non voluta costruzione. Inoltre, quanto spesso hai bisogno di manipolare le stringhe su una passphrase? :) – snemarch

Problemi correlati