Recentemente mi sono imbattutto nella spinosa problematica di gestire la protezione e la cifratura di dati sensibili (dati sanitari vi ho detto tutto ;)) durante delle elaborazioni in memoria da parte di più applicazioni distribuite. Tutto nasce dal requisito di rendere illeggibili i dati anche nel caso in cui vengano messi sotto analisi dei file di dump della memoria a seguito, ad esempio, di un crash imprevisto di un processo... senza poi considerare possibili 'memory probings' da parte di debugger maliziosi!
Una classe del Framework 2.0 che in questo senso mi sta facendo dormire abbastanza (*) tranquillo è la classe ProtectedMemory del namespace System.Security.Cryptography. Questa classe costituisce uno dei wrapper managed delle ormai famose DPAPI (Data Protection API). Il principio di funzionamento è praticamente identico a quello del suo fratello (o sorella?) ProtectedData, che invece andrebbe utilizzata per la persistenza di dati cifrati su file/database: vengono esposti due metodi statici, Protect e Unprotect, che permettono rispettivamente di cifrare e decifrare dei dati in memoria. L'unica accortezza a cui dobbiamo attenerci per evitare una bella CryptographicException è l'elaborazione di bytes in blocchi di 16 o multipli. Ecco un esempio di utilizzo su una stringa con encoding UTF-8:
using System.Security.Cryptography;
...
string OriginalString = "xxxxxxxxxxxxxxxx";
Console.WriteLine("Before Protect: " + OriginalString);
byte[] OriginalBytes = Encoding.UTF8.GetBytes(OriginalString);
ProtectedMemory.Protect(OriginalBytes, MemoryProtectionScope.CrossProcess);
UTF8Encoding enc = new UTF8Encoding();
Console.WriteLine("After Protect: " + enc.GetString(OriginalBytes));
ProtectedMemory.Unprotect(OriginalBytes, MemoryProtectionScope.CrossProcess);
OriginalString = Encoding.UTF8.GetString(OriginalBytes);
Console.WriteLine("After Unprotect: " + OriginalString);
L'output che otteniamo è il seguente:
Before Protect: xxxxxxxxxxxxxxxx
After Protect: 7j??‼8y_??PQ<?¶-
After Unprotect: xxxxxxxxxxxxxxxx
E' molto importante rendersi conto del fatto che le operazioni di Protect e Unprotect lavorano direttamente sui dati originali, quindi non necessitiamo di alcuna distruzione esplicita degli stessi in quanto non viene generata alcuna copia fisica.
Alternativamente si potebbe utilizzare la SecureString, una classe di più intuitivo utilizzo che, sfruttando proprio le funzionalità di ProtectedMemory, automaticamente rappresenta stringhe cifrate, modificabili (a meno di MakeReadOnly()) e 'spianabili' programmaticamente, ma con un'unica interessante limitazione: possiede come MemoryProtectionScope il valore SameProcess. In poche parole, non sono utilizzabili in scenari CrossProcess e 'SameLogon', ovvero in quei casi in cui ci sono più applicazioni in esecuzione su processi diversi (CrossProcess) che permettono la cifratura/decifratura dei dati soltanto allo stesso utente di Windows (per ovviare, eventualmente potremmo prendere - a nostro rischio e pericolo - la via dell' impersonation ;)).
* Un comportamento strano di cui mi sono accorto analizzando saltuariamente il dump della memoria è il seguente: alcune volte il testo che intendo proteggere appare in chiaro!!! Forse esiste la possibilità che Windows operi uno swap su disco del processo mentre i dati non sono ancora cifrati??? In questo caso tutti i miei sforzi sembrerebbero non servire a nulla ?!? MAH...
Technorati tags:
ProtectedMemory ,
DPAPI