Quando avrei bisogno di un SecureString in .NET?


179

Sto cercando di capire lo scopo di SecureString di .NET. Da MSDN:

Un'istanza della classe System.String è sia immutabile che, quando non è più necessaria, non può essere programmata programmaticamente per la garbage collection; vale a dire, l'istanza è di sola lettura dopo la sua creazione e non è possibile prevedere quando l'istanza verrà eliminata dalla memoria del computer. Di conseguenza, se un oggetto String contiene informazioni riservate come una password, un numero di carta di credito o dati personali, esiste il rischio che le informazioni possano essere rivelate dopo essere state utilizzate poiché l'applicazione non può eliminare i dati dalla memoria del computer.

Un oggetto SecureString è simile a un oggetto String in quanto ha un valore di testo. Tuttavia, il valore di un oggetto SecureString viene automaticamente crittografato, può essere modificato fino a quando l'applicazione non lo contrassegna come di sola lettura e può essere eliminato dalla memoria del computer dall'applicazione o dal Garbage Collector di .NET Framework.

Il valore di un'istanza di SecureString viene automaticamente crittografato quando l'istanza viene inizializzata o quando il valore viene modificato. L'applicazione può rendere l'istanza immutabile e impedire ulteriori modifiche invocando il metodo MakeReadOnly.

La crittografia automatica è il grande vantaggio?

E perché non posso semplicemente dire:

SecureString password = new SecureString("password");

invece di

SecureString pass = new SecureString();
foreach (char c in "password".ToCharArray())
    pass.AppendChar(c);

Quale aspetto di SecureString mi manca?


3
11 anni dopo e MS non consiglia più SecureStringper il nuovo sviluppo: github.com/dotnet/platform-compat/blob/master/docs/DE0001.md
Matt Thomas

Risposte:


4

Smetterei di usare SecureString. Sembra che i ragazzi PG stiano abbandonando il supporto per questo. Forse anche tirarlo in futuro - https://github.com/dotnet/apireviews/tree/master/2015-07-14-securestring .

Dovremmo rimuovere la crittografia da SecureString su tutte le piattaforme in .NET Core - Dovremmo obsoleto SecureString - Probabilmente non dovremmo esporre SecureString in .NET Core


1
Il link è morto ma sembra continuare: docs.microsoft.com/en-us/dotnet/api/… .. con alcune indicazioni molto deboli sul percorso in avanti esegui "non usare credenziali" - github.com/dotnet/platform- compat / blob / master / docs / DE0001.md .. non osare nemmeno usare una password per proteggere la chiave privata dei certificati!
Felickz,

1
Dopo 11 anni, questa risposta sembra ora essere la "nuova" corretta. I collegamenti sembrano stantii ma la guida di MS è: SecureString non dovrebbe essere usato
Richard Morgan,

109

Alcune parti del framework che attualmente utilizzano SecureString:

Lo scopo principale è ridurre la superficie di attacco, piuttosto che eliminarla. SecureStringssono "bloccati" nella RAM in modo che Garbage Collector non lo sposti o ne faccia copie. Assicura inoltre che il testo in chiaro non venga scritto nel file Swap o nei dump principali. La crittografia è più simile all'offuscamento e non fermerà un determinato hacker, che sarebbe in grado di trovare la chiave simmetrica utilizzata per crittografarlo e decrittografarlo.

Come altri hanno già detto, il motivo per cui devi creare un SecureStringcarattere per carattere è dovuto al primo ovvio difetto di fare diversamente: presumibilmente hai già il valore segreto come una semplice stringa, quindi qual è il punto?

SecureStrings sono il primo passo per risolvere un problema Chicken-and-Egg, quindi anche se la maggior parte degli scenari attuali richiede di convertirli in stringhe regolari per farne un uso, la loro esistenza nel framework ora significa un migliore supporto per loro nel futuro - almeno fino al punto in cui il tuo programma non deve essere l'anello debole.


2
Mi sono imbattuto in esso guardando la proprietà Password di ProcessStartInfo; nemmeno prestando attenzione al tipo, l'ho semplicemente impostato su una stringa normale fino a quando il compilatore non mi ha abbaiato.
Richard Morgan,

Non sarà facile trovare la chiave di crittografia simmetrica, poiché SecureString si basa su DPAPI, che non memorizza esattamente una chiave in testo normale ...
AviD

1
Inoltre, non è tanto il problema dell'uovo e della gallina, dal momento che non è un sostituto della crittografia nell'archiviazione, ma è una soluzione alternativa per stringhe .NET gestite immutabili.
AviD,

2
"presumibilmente hai già il valore segreto come semplice stringa, quindi qual è il punto?" C'è una risposta a questa domanda? Nel migliore dei casi sembra una soluzione "meglio di niente" se si intende conservare una password in memoria per un periodo prolungato.
xr280xr,

2
Che ne dici di avere alcuni semplici esempi di codice di utilizzo? Credo che capirei meglio come e quando usarlo.
codea,

37

Modifica : non utilizzare SecureString

La guida attuale ora dice che la classe non dovrebbe essere usata. I dettagli sono disponibili a questo link: https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md

Dall'articolo:

DE0001: SecureString non deve essere utilizzato

Motivazione

  • Lo scopo SecureStringè quello di evitare che i segreti siano archiviati nella memoria di processo come testo normale.
  • Tuttavia, anche su Windows, SecureStringnon esiste un concetto di sistema operativo.
    • Rende solo la finestra che accorcia il testo in chiaro; non lo impedisce del tutto poiché .NET deve ancora convertire la stringa in una rappresentazione in testo semplice.
    • Il vantaggio è che la rappresentazione in testo semplice non viene visualizzata come un'istanza di System.String: la durata del buffer nativo è più breve.
  • Il contenuto dell'array non è crittografato ad eccezione di .NET Framework.
    • In .NET Framework, il contenuto dell'array di caratteri interno è crittografato. .NET non supporta la crittografia in tutti gli ambienti, a causa di API mancanti o problemi di gestione delle chiavi.

Raccomandazione

Non utilizzare SecureStringper il nuovo codice. Quando si esegue il porting del codice su .NET Core, tenere presente che i contenuti dell'array non sono crittografati in memoria.

L'approccio generale di gestione delle credenziali è quello di evitarle e invece fare affidamento su altri mezzi per l'autenticazione, come i certificati o l'autenticazione di Windows.

Fine modifica: riepilogo originale di seguito

Molte grandi risposte; ecco una breve sintesi di ciò che è stato discusso.

Microsoft ha implementato la classe SecureString nel tentativo di fornire una migliore sicurezza con informazioni sensibili (come carte di credito, password, ecc.). Fornisce automaticamente:

  • crittografia (in caso di dump della memoria o memorizzazione nella cache delle pagine)
  • appuntare in memoria
  • capacità di contrassegnare come di sola lettura (per evitare ulteriori modifiche)
  • costruzione sicura NON consentendo il passaggio di una stringa costante

Attualmente, SecureString è limitato nell'uso ma prevede una migliore adozione in futuro.

Sulla base di queste informazioni, il costruttore di SecureString non dovrebbe semplicemente prendere una stringa e suddividerla in array di caratteri poiché la stringa spiegata vanifica lo scopo di SecureString.

Informazioni addizionali:

  • Un post dal blog di .NET Security che parla più o meno come descritto qui.
  • E un altro una rivisitazione e citano uno strumento che può scaricare il contenuto del SecureString.

Modifica: ho trovato difficile scegliere la risposta migliore in quanto ci sono buone informazioni in molti; peccato che non ci siano opzioni di risposta assistita.


19

Risposta breve

perché non posso semplicemente dire:

SecureString password = new SecureString("password");

Perché ora hai passwordin memoria; senza alcun modo per cancellarlo, che è esattamente il punto di SecureString .

Risposta lunga

Il motivo per cui esiste SecureString è perché non è possibile utilizzare ZeroMemory per cancellare i dati sensibili al termine. Esiste per risolvere un problema esistente a causa del CLR.

In una normale applicazione nativa chiameresti SecureZeroMemory:

Riempie un blocco di memoria con zeri.

Nota : SecureZeroMemory è identico a ZeroMemory, tranne per il fatto che il compilatore non lo ottimizzerà.

Il problema è che non puoi chiamare ZeroMemoryo SecureZeroMemoryall'interno di .NET. E in .NET le stringhe sono immutabili; non puoi nemmeno sovrascrivere il contenuto della stringa come puoi fare in altre lingue:

//Wipe out the password
for (int i=0; i<password.Length; i++)
   password[i] = \0;

Che cosa si può fare? Come possiamo fornire in .NET la possibilità di cancellare una password o il numero di una carta di credito dalla memoria quando abbiamo finito?

L'unico modo per farlo sarebbe quello di posizionare la stringa in un blocco di memoria nativo , dove è possibile chiamare ZeroMemory. Un oggetto memoria nativa come:

  • un BSTR
  • un HGLOBAL
  • CoTaskMem memoria non gestita

SecureString restituisce l'abilità persa

In .NET, le stringhe non possono essere cancellate quando hai finito con loro:

  • sono immutabili; non puoi sovrascriverne il contenuto
  • non puoi Disposeda loro
  • la loro pulizia è in balia del garbage collector

SecureString esiste come un modo per aggirare la sicurezza delle stringhe ed essere in grado di garantirne la pulizia quando è necessario.

Hai fatto la domanda:

perché non posso semplicemente dire:

SecureString password = new SecureString("password");

Perché ora hai passwordin memoria; senza alcun modo per cancellarlo. È bloccato lì fino a quando il CLR non decide di riutilizzare quella memoria. Ci hai riportato proprio dove abbiamo iniziato; un'applicazione in esecuzione con una password di cui non possiamo liberarci e in cui un dump della memoria (o Process Monitor) può vedere la password.

SecureString utilizza l'API Data Protection per archiviare la stringa crittografata in memoria; in questo modo la stringa non esisterà nei file di scambio, nei dump di arresto anomalo o nella finestra delle variabili locali con un collega che osserva il tuo dovere.

Come leggo la password?

Quindi la domanda è: come interagisco con la stringa? È assolutamente non si desidera che un metodo come:

String connectionString = secureConnectionString.ToString()

perché ora sei tornato da dove hai iniziato: una password di cui non puoi liberarti. Volete costringere gli sviluppatori a gestire correttamente la stringa sensibile, in modo che possa essere cancellata dalla memoria.

Ecco perché .NET offre tre utili funzioni di supporto per eseguire il marshalling di un SecureString in una memoria non gestita:

Convertire la stringa in un BLOB di memoria non gestito, gestirla e quindi cancellarla di nuovo.

Alcune API accettano SecureStrings . Ad esempio in ADO.net 4.5 SqlConnection.Credential accetta un set SqlCredential :

SqlCredential cred = new SqlCredential(userid, password); //password is SecureString
SqlConnection conn = new SqlConnection(connectionString);
conn.Credential = cred;
conn.Open();

Puoi anche modificare la password all'interno di una stringa di connessione:

SqlConnection.ChangePassword(connectionString, cred, newPassword);

E ci sono molti posti all'interno di .NET in cui continuano ad accettare una semplice stringa a fini di compatibilità, quindi si trasformano rapidamente e la mettono in un SecureString.

Come inserire il testo in SecureString?

Questo lascia ancora il problema:

Come posso ottenere una password in SecureString in primo luogo?

Questa è la sfida, ma il punto è farti pensare alla sicurezza.

A volte la funzionalità è già fornita per te. Ad esempio, il controllo PasswordP WPF può restituire direttamente la password immessa come SecureString :

Proprietà PasswordBox.SecurePassword

Ottiene la password attualmente detenuta da PasswordBox come SecureString .

Questo è utile perché ovunque passavi intorno a una stringa non elaborata, ora hai il sistema di tipo che si lamenta che SecureString è incompatibile con String. Vuoi andare il più a lungo possibile prima di dover riconvertire SecureString in una stringa normale.

La conversione di un SecureString è abbastanza semplice:

  • SecureStringToBSTR
  • PtrToStringBSTR

come in:

private static string CreateString(SecureString secureString)
{
    IntPtr intPtr = IntPtr.Zero;
    if (secureString == null || secureString.Length == 0)
    {
        return string.Empty;
    }
    string result;
    try
    {
        intPtr = Marshal.SecureStringToBSTR(secureString);
        result = Marshal.PtrToStringBSTR(intPtr);
    }
    finally
    {
        if (intPtr != IntPtr.Zero)
        {
            Marshal.ZeroFreeBSTR(intPtr);
        }
    }
    return result;
}

Semplicemente non vogliono che tu lo faccia.

Ma come posso ottenere una stringa in un SecureString? Bene, quello che devi fare è smettere di avere una password in una stringa in primo luogo. È necessario avere in qualcosa d'altro. Anche un Char[]array sarebbe utile.

Ecco quando puoi aggiungere ogni personaggio e cancellare il testo in chiaro al termine:

for (int i=0; i < PasswordArray.Length; i++)
{
   password.AppendChar(PasswordArray[i]);
   PasswordArray[i] = (Char)0;
}

È necessario che la password sia memorizzata in un po 'di memoria da poter cancellare. Caricalo nel SecureString da lì.


tl; dr: esiste SecureString per fornire l'equivalente di ZeroMemory .

Alcune persone non vedono il punto nel cancellare la password dell'utente dalla memoria quando un dispositivo è bloccato o nel cancellare i tasti premuti dalla memoria dopo che sono stati autenticati . Quelle persone non usano SecureString.


14

Esistono pochissimi scenari in cui è possibile utilizzare in modo ragionevole SecureString nella versione corrente di Framework. È davvero utile solo per interagire con le API non gestite: puoi eseguirne il marshalling utilizzando Marshal.SecureStringToGlobalAllocUnicode.

Non appena lo converti in / da un System.String, hai annullato il suo scopo.

L' esempio MSDN genera un carattere alla volta SecureString dall'input della console e passa la stringa protetta a un'API non gestita. È piuttosto contorto e irrealistico.

Potresti aspettarti che le future versioni di .NET dispongano di più supporto per SecureString che lo renderà più utile, ad esempio:

  • SecureString Console.ReadLineSecure () o simile per leggere l'input della console in un SecureString senza tutto il codice contorto nell'esempio.

  • Sostituzione TextBox WinForms che memorizza la sua proprietà TextBox.Text come stringa sicura in modo che le password possano essere inserite in modo sicuro.

  • Estensioni alle API relative alla sicurezza per consentire il passaggio delle password come SecureString.

Senza quanto sopra, SecureString avrà un valore limitato.


12

Credo che il motivo per cui è necessario eseguire l'aggiunta di caratteri anziché un'istanza semplice sia perché in background il passaggio di "password" al costruttore di SecureString inserisce quella stringa "password" in memoria vanificando lo scopo di stringa sicura.

Aggiungendo alla memoria stai solo mettendo un personaggio alla volta che è come non essere adiacenti l'uno all'altro, rendendo molto più difficile ricostruire la stringa originale. Potrei sbagliarmi qui, ma è così che mi è stato spiegato.

Lo scopo della classe è impedire che i dati sicuri vengano esposti tramite un dump della memoria o uno strumento simile.


11

MS ha scoperto che in alcuni casi in cui il server (desktop, qualunque cosa) si arrestasse in modo anomalo, c'erano volte in cui l'ambiente di runtime eseguiva un dump della memoria esponendo il contenuto di ciò che è in memoria. Secure String lo crittografa in memoria per impedire all'autore dell'attacco di recuperare il contenuto della stringa.


5

Uno dei maggiori vantaggi di un SecureString è che si suppone che si eviti la possibilità che i dati vengano archiviati su disco a causa della memorizzazione nella cache della pagina. Se si dispone di una password in memoria e quindi si carica un programma o un set di dati di grandi dimensioni, è possibile che la password venga scritta nel file di scambio man mano che il programma viene esaurito nella memoria. Con un SecureString, almeno i dati non rimarranno indefinitamente sul disco in chiaro.


4

Immagino sia perché la stringa deve essere sicura, cioè un hacker non dovrebbe essere in grado di leggerlo. Se lo inizializzi con una stringa, l'hacker potrebbe leggere la stringa originale.


4

Bene, come afferma la descrizione, il valore viene archiviato crittografato, con un dump di memoria del processo che non rivelerà il valore della stringa (senza un lavoro abbastanza serio).

Il motivo per cui non puoi semplicemente costruire un SecureString da una stringa costante è perché allora avresti una versione non crittografata della stringa in memoria. Limitare la creazione della stringa in pezzi riduce il rischio di avere l'intera stringa in memoria contemporaneamente.


2
Se stanno limitando la costruzione da una stringa costante, allora la riga foreach (char c in "password" .ToCharArray ()) lo sconfiggerebbe, no? Dovrebbe essere pass.AppendChar ('p'); pass.AppendChar ( 'a'); ecc?
Richard Morgan,

Sì, puoi facilmente eliminare la poca protezione che SecureString ti offre. Stanno cercando di rendere difficile spararsi completamente ai piedi. Ovviamente ci deve essere un modo per ottenere il valore dentro e fuori da un SecureString, altrimenti non potresti usarlo per nulla.
Mark Bessey,

1

Un altro caso d'uso è quando si lavora con applicazioni di pagamento (POS) e semplicemente non è possibile utilizzare strutture di dati immutabili per archiviare dati sensibili perché si è attenti sviluppatori. Ad esempio: se memorizzerò i dati sensibili delle carte o i metadati di autorizzazione in una stringa immutabile, ci sarebbe sempre il caso in cui questi dati saranno disponibili in memoria per un periodo di tempo significativo dopo che sono stati scartati. Non posso semplicemente sovrascriverlo. Un altro enorme vantaggio in cui tali dati sensibili vengono conservati in memoria crittografati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.