|
La classe System.String non rappresenta la soluzione più sicura quando è
necessario memorizzare sotto forma di stringa contenuti confidenziali, ad
esempio stringhe di connessione a database, password varie, numeri di carta di
credito, ecc.. Informazioni di questo genere andrebbero mantenute in memoria
solo il tempo strettamente necessario alla loro elaborazione, e la memoria
occupata andrebbe rilasciata il più presto possibile. Questo accorgimento è
necessario poichè il contenuto della memoria utilizzata da un processo in
esecuzione potrebbe essere letta attraverso varie tecniche di hacking, e quindi
il suo eventuale contenuto confidenziale correrebbe seri pericoli di
manomissione. Inoltre, un processo in esecuzione potrebbe utilizzare un file di
swap su disco che provocherebbe la scrittura anche di questo genere di
informazioni.
[Perchè una stringa è insicura] La classe
System.String non si adatta affatto a queste caratteristiche. Infatti, una
stringa è un oggetto immutabile, per cui ogni modifica ad essa comporta la
creazione di una nuova stringa memorizzata in un’area di memoria differente,
mentre la vecchia stringa continuerà ad esistere fino alla successiva operazione
di garbage collection, che, a causa della sua natura non deterministica,
potrebbe avvenire ben più tardi rispetto al momento in cui tutti i riferimenti
ad essa sono rilasciati. L’immutabilità delle stringhe costituisce un serio
problema se una stringa contiene informazioni confidenziali per almeno 3
motivi:
• Ogni manipolazione effettuata su di essa lascia in memoria
una copia della stessa, in attesa del garbage collector; • Non
esiste alcun modo per “resettare” il contenuto della stringa una volta
utilizzato; il tentativo di assegnare un valore nullo provocherà la
creazione di un nuovo oggetto stringa con il nuovo contenuto lasciando
invariato il vecchio; • L’indirizzo di memoria in cui è memorizzata una
stringa non è costante una volta creata; • Non è possibile criptare il
contenuto di una stringa senza provocare la creazione di un nuovo
oggetto con il contenuto criptato;
[La soluzione in .NET
1.0/1.1] Nella versione 1.0/1.1 del Microsoft .NET Framework non esiste
una soluzione semplice a questo problema. Infatti l’unica strada percorribile è
quella di criptare/decriptare manualmente il contenuto di una stringa
confidenziale utilizzando un array di caratteri come area di lavoro. Non è
invece possibile resettare una stringa senza attendere che il garbage collector
agisca.
[La soluzione in .NET 2.0] La versione
2.0 del .NET Framework pone rimedio a tutti i problemi di sicurezza
precedentemente elencati attraverso la nuova classe SecureString presente nel
namespace System.Security. Questa nuova classe permette di memorizzare in
modo sicuro e criptato una stringa contenente contenuto confidenziale,
utilizzando DPAPI (Data Protection API), ovvero una serie di API per il
criptaggio ed il decriptaggio di informazioni mediante l’algoritmo
Triple-DES.
Ecco un esempio di utilizzo:
 Come si può notare, l’utilizzo di SecureString è un po’ più
macchinoso rispetto alla creazione di un semplice oggetto stringa; infatti esso
richiede che la stringa sicura sia costruita aggiungendo un carattere alla volta
attraverso il metodo AppendChar(). Il motivo di questo utilizzo dovrebbe essere
ovvio: non è possibile costruire una stringa sicura a partire da una stringa già
in memoria in testo in chiaro, poiché quest’ultima sarebbe immutabile con gli
identici problemi di sicurezza visti precedentemente. Inoltre, una istanza di
SecureString è mutabile, il suo indirizzo di memoria non cambia una volta creata
e può quindi facilmente essere azzerata attraverso il metodo Clear() senza
attendere il garbage collector. Oltre al metodo AppendChar() esistono altri
metodi per modificare il contenuto, ad es. InsertAt(), RemoveAt(), SetAt(), il
cui nome è abbastanza intuitivo per comprendere il loro utilizzo. La proprietà
MakeReadOnly blocca il contenuto dell’oggetto SecureString non permettendo più
alcuna modifica. Questa caratteristica può essere letta attraverso la proprietà
IsReadOnly. Inoltre, la classe SecureString implementa l’interfaccia
IDisposable, ovvero permette al suo utilizzatore di distruggere l’istanza in
memoria senza attendere il garbage collector.
Dopo aver creato e popolato una istanza della
classe SecureString occorre leggere il valore in essa contenuto. Questa
operazione non può essere fatta richiamando semplicemente il metodo ToString()
della classe, in quanto quest’ultimo non è stato ridefinito e ritorna
semplicemente l’oggetto sotto forma di stringa così come ereditato da
System.Object, vale a dire “System.Security.SecureString”, non di grande
utilità, e non è semplicissima in quanto comporta una operazione di marshalling
(attraverso l’utilizzo della classe Marshal presente nel namespace
System.Runtime.InteropServices) che coinvolge l’utilizzo di un puntatore alla
stringa, anche se il tutto si risolve con poche righe di codice, come in questo
esempio:
 Una volta
ottenuta la controparte immutabile della stringa, è necessario assicurarsi che
l’area di memoria puntata dal puntatore sia azzerata attraverso l’utilizzo del
metodo ZeroFreeBSTR della classe
Marshal.
[Riferimenti] [1] Maurizio Tammacco -
Pinned object in .NET [1]
Maurizio Tammacco - Uso efficiente delle
stringhe in .NET
|