Come convertire SecureString in System.String?


156

Tutte le prenotazioni circa unsecuring tua SecureString creando uno System.String fuori di esso da parte , come può essere fatto?

Come posso convertire un normale System.Security.SecureString in System.String?

Sono sicuro che molti di voi che hanno familiarità con SecureString risponderanno che non si dovrebbe mai trasformare un SecureString in una normale stringa .NET perché rimuove tutte le protezioni di sicurezza. Lo so . Ma in questo momento il mio programma fa tutto con le stringhe ordinarie in ogni caso, e sto cercando di migliorare la sua sicurezza e, anche se ho intenzione di utilizzare un'API che restituisce un SecureString a me sto non cercando di usarlo per aumentare la mia sicurezza.

Sono a conoscenza di Marshal.SecureStringToBSTR, ma non so come prendere quel BSTR e creare un System.String da esso.

Per coloro che potrebbero chiedere di sapere perché mai avrei voluto farlo, sto prendendo una password da un utente e la invio come un modulo HTML POST per accedere all'utente in un sito web. Quindi ... questo deve davvero essere fatto con buffer gestiti, non crittografati. Se potessi anche ottenere l'accesso al buffer non gestito e non crittografato, immagino di poter scrivere byte per byte sul flusso di rete e sperare che mantenga la password sicura per tutto il tempo. Spero in una risposta ad almeno uno di questi scenari.

Risposte:


192

Usa la System.Runtime.InteropServices.Marshalclasse:

String SecureStringToString(SecureString value) {
  IntPtr valuePtr = IntPtr.Zero;
  try {
    valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
    return Marshal.PtrToStringUni(valuePtr);
  } finally {
    Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
  }
}

Se si desidera evitare la creazione di un oggetto stringa gestito, è possibile accedere ai dati non elaborati utilizzando Marshal.ReadInt16(IntPtr, Int32):

void HandleSecureString(SecureString value) {
  IntPtr valuePtr = IntPtr.Zero;
  try {
    valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
    for (int i=0; i < value.Length; i++) {
      short unicodeChar = Marshal.ReadInt16(valuePtr, i*2);
      // handle unicodeChar
    }
  } finally {
    Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
  }
}

1
Ho ottenuto il mio voto troppo anche anni dopo, grazie per l'aiuto! Solo una breve nota: funziona anche come statico, nella sua memoria.
John Suit,

Ho usato StopWatche SecureStringToStringpreso 4.6sec per l'esecuzione. È rallentare per me. Qualcuno ottiene lo stesso tempo o qualcosa di più veloce?
Radbyx,

@radbyx In una configurazione di test rapida e sporca, posso chiamarlo 1000 volte in 76ms. La prima chiamata richiede 0,3 ms e le successive chiamate ~ 0,07 ms. Quanto è grande la tua stringa sicura e quale versione del framework stai usando?
Rasmus Faber,

La lunghezza del mio secureString è 168. Sto usando .NET Framework 3.5 se ha risposto alla tua domanda? Ho provato 5-10 volte è sempre intorno ai 4,5-4,65 secondi ~ Mi piacerebbe molto avere il tuo tempo
radbyx,

@RasmusFaber Mio male, avevo aggiunto una Database.GetConnectionString()al tuo codice, per ottenere il mio secureString, che era la parte malvagia che impiegava quasi 5 secondi (e sì, dovrei esaminarla! :) Il tuo codice ha impiegato 0,00 milioni di secondi nel mio cronometro, quindi è tutto bene. Grazie per avermi indicato nella giusta direzione.
Radbyx,

108

Ovviamente sai come questo sconfigge l'intero scopo di un SecureString, ma lo riaffermerò comunque.

Se si desidera un one-liner, provare questo: (solo .NET 4 e versioni successive)

string password = new System.Net.NetworkCredential(string.Empty, securePassword).Password;

Dove securePassword è un SecureString.


10
Sebbene vanifichi lo scopo della produzione, la tua soluzione è perfetta per i test unitari. Grazie.
Beterthanlife,

Questo mi ha aiutato a capire che un SecureString (System.Security.SecureString) non veniva passato al mio ApiController (webapi). Thx
granadaCoder

5
Nota in PowerShell questo è[System.Net.NetworkCredential]::new('', $securePassword).Password
stijn

1
@ TheIncorrigible1 puoi elaborare? Ad esempio, quando ''non è lo stesso tipo di [String]::Empty? Inoltre New-Object Net.Credentialnon funziona per me: impossibile trovare il tipo [Net.Credential]: verificare che l'assembly contenente questo tipo sia caricato
data

2
Sconfigge lo scopo di un SecureString perché crea una copia non crittografata del contenuto di SecureString in una stringa normale. Ogni volta che lo fai, stai aggiungendo almeno una (e con Garbage Collection forse più copie) della tua stringa non crittografata alla memoria. Questo è considerato un rischio per alcune applicazioni sensibili alla sicurezza e SecureString è stato implementato specificamente per ridurre il rischio.
Steve In CO,

49

Dang. subito dopo aver pubblicato questo ho trovato la risposta in profondità in questo articolo . Ma se qualcuno sa come accedere al buffer non gestito e non crittografato di IntPtr che espone questo metodo, un byte alla volta in modo che non debba creare un oggetto stringa gestito da esso per mantenere alta la mia sicurezza, si prega di aggiungere una risposta. :)

static String SecureStringToString(SecureString value)
{
    IntPtr bstr = Marshal.SecureStringToBSTR(value);

    try
    {
        return Marshal.PtrToStringBSTR(bstr);
    }
    finally
    {
        Marshal.FreeBSTR(bstr);
    }
}

Puoi certamente usare la unsafeparola chiave e un char*, basta chiamare bstr.ToPointer()e trasmettere.
Ben Voigt,

@BenVoigt BSTR ha un terminatore null dopo i dati della stringa per sicurezza, ma consente anche caratteri null incorporati nella stringa. Quindi è un po 'più complicato di così, devi anche recuperare il prefisso di lunghezza che si trova prima di quel puntatore. docs.microsoft.com/en-us/previous-versions/windows/desktop/…
Wim Coenen

@WimCoenen: vero ma non importante. La lunghezza memorizzata nel BSTR sarà una copia della lunghezza già disponibile da SecureString.Length.
Ben Voigt,

@BenVoigt ah, il mio male. Pensavo che SecureString non avesse rivelato alcuna informazione sulla stringa.
Wim Coenen,

@WimCoenen: SecureStringnon sta cercando di nascondere il valore, sta cercando di impedire che copie del valore vengano trasformate in aree che non possono essere sovrascritte in modo affidabile, come la memoria raccolta rifiuti, il file di paging, ecc. L'intenzione è che quando la SecureStringvita finisce, assolutamente nessuna copia del segreto rimane in memoria. Non ti impedisce di fare e perdere una copia, ma non lo fa mai.
Ben Voigt,

15

Secondo me, metodi di estensione sono il modo più comodo per risolverlo.

Ho preso Steve nella risposta eccellente di CO e l'ho inserito in una classe di estensione come segue, insieme a un secondo metodo che ho aggiunto per supportare anche l'altra direzione (stringa -> stringa sicura), in modo da poter creare una stringa sicura e convertirla in una stringa normale in seguito:

public static class Extensions
{
    // convert a secure string into a normal plain text string
    public static String ToPlainString(this System.Security.SecureString secureStr)
    {
        String plainStr=new System.Net.NetworkCredential(string.Empty, secureStr).Password;
        return plainStr;
    }

    // convert a plain text string into a secure string
    public static System.Security.SecureString ToSecureString(this String plainStr)
    {
        var secStr = new System.Security.SecureString(); secStr.Clear();
        foreach (char c in plainStr.ToCharArray())
        {
            secStr.AppendChar(c);
        }
        return secStr;
    }
}

Con questo, ora puoi semplicemente convertire le tue stringhe avanti e indietro in questo modo:

// create a secure string
System.Security.SecureString securePassword = "MyCleverPwd123".ToSecureString(); 
// convert it back to plain text
String plainPassword = securePassword.ToPlainString();  // convert back to normal string

Ma tieni presente che il metodo di decodifica dovrebbe essere usato solo per i test.


14

Penso che sarebbe meglio incapsulareSecureString funzioni dipendenti loro logica dipendente in una funzione anonima per un migliore controllo sulla stringa decifrata in memoria (una volta bloccata).

L'implementazione per la decrittografia di SecureStrings in questo frammento:

  1. Pin la stringa in memoria (che è quello che vuoi fare ma sembra mancare dalla maggior parte delle risposte qui).
  2. Passare il riferimento al delegato Func / Action.
  3. Sfregalo dalla memoria e rilascia il GC nel finallyblocco.

Questo ovviamente rende molto più facile "standardizzare" e mantenere i chiamanti rispetto al fatto di fare affidamento su alternative meno desiderabili:

  • Restituisce la stringa decifrata da una string DecryptSecureString(...)funzione di supporto.
  • Duplicazione di questo codice ovunque sia necessario.

Nota qui, hai due opzioni:

  1. static T DecryptSecureString<T>che consente di accedere al risultato del Funcdelegato dal chiamante (come mostrato nel DecryptSecureStringWithFuncmetodo di prova).
  2. static void DecryptSecureStringè semplicemente una versione "nulla" che impiega un Actiondelegato nei casi in cui in realtà non si desidera / non è necessario restituire nulla (come dimostrato nel DecryptSecureStringWithActionmetodo di test).

Un esempio di utilizzo per entrambi può essere trovato nella StringsTestclasse inclusa.

Strings.cs

using System;
using System.Runtime.InteropServices;
using System.Security;

namespace SecurityUtils
{
    public partial class Strings
    {
        /// <summary>
        /// Passes decrypted password String pinned in memory to Func delegate scrubbed on return.
        /// </summary>
        /// <typeparam name="T">Generic type returned by Func delegate</typeparam>
        /// <param name="action">Func delegate which will receive the decrypted password pinned in memory as a String object</param>
        /// <returns>Result of Func delegate</returns>
        public static T DecryptSecureString<T>(SecureString secureString, Func<string, T> action)
        {
            var insecureStringPointer = IntPtr.Zero;
            var insecureString = String.Empty;
            var gcHandler = GCHandle.Alloc(insecureString, GCHandleType.Pinned);

            try
            {
                insecureStringPointer = Marshal.SecureStringToGlobalAllocUnicode(secureString);
                insecureString = Marshal.PtrToStringUni(insecureStringPointer);

                return action(insecureString);
            }
            finally
            {
                //clear memory immediately - don't wait for garbage collector
                fixed(char* ptr = insecureString )
                {
                    for(int i = 0; i < insecureString.Length; i++)
                    {
                        ptr[i] = '\0';
                    }
                }

                insecureString = null;

                gcHandler.Free();
                Marshal.ZeroFreeGlobalAllocUnicode(insecureStringPointer);
            }
        }

        /// <summary>
        /// Runs DecryptSecureString with support for Action to leverage void return type
        /// </summary>
        /// <param name="secureString"></param>
        /// <param name="action"></param>
        public static void DecryptSecureString(SecureString secureString, Action<string> action)
        {
            DecryptSecureString<int>(secureString, (s) =>
            {
                action(s);
                return 0;
            });
        }
    }
}

StringsTest.cs

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Security;

namespace SecurityUtils.Test
{
    [TestClass]
    public class StringsTest
    {
        [TestMethod]
        public void DecryptSecureStringWithFunc()
        {
            // Arrange
            var secureString = new SecureString();

            foreach (var c in "UserPassword123".ToCharArray())
                secureString.AppendChar(c);

            secureString.MakeReadOnly();

            // Act
            var result = Strings.DecryptSecureString<bool>(secureString, (password) =>
            {
                return password.Equals("UserPassword123");
            });

            // Assert
            Assert.IsTrue(result);
        }

        [TestMethod]
        public void DecryptSecureStringWithAction()
        {
            // Arrange
            var secureString = new SecureString();

            foreach (var c in "UserPassword123".ToCharArray())
                secureString.AppendChar(c);

            secureString.MakeReadOnly();

            // Act
            var result = false;

            Strings.DecryptSecureString(secureString, (password) =>
            {
                result = password.Equals("UserPassword123");
            });

            // Assert
            Assert.IsTrue(result);
        }
    }
}

Ovviamente, ciò non impedisce l'abuso di questa funzione nel modo seguente, quindi fai attenzione a non farlo:

[TestMethod]
public void DecryptSecureStringWithAction()
{
    // Arrange
    var secureString = new SecureString();

    foreach (var c in "UserPassword123".ToCharArray())
        secureString.AppendChar(c);

    secureString.MakeReadOnly();

    // Act
    string copyPassword = null;

    Strings.DecryptSecureString(secureString, (password) =>
    {
        copyPassword = password; // Please don't do this!
    });

    // Assert
    Assert.IsNull(copyPassword); // Fails
}

Buona programmazione!


Perché non usare al Marshal.Copy(new byte[insecureString.Length], 0, insecureStringPointer, (int)insecureString.Length);posto della fixedsezione?
sclarke81,

@ sclarke81, buona idea, ma dovrai usare [char]no [byte].
mklement0

1
L'approccio generale è promettente, ma non credo che il tuo tentativo di bloccare la stringa gestita che contiene la copia non sicura (testo normale) sia efficace: quello che stai bloccando invece è l' oggetto stringa originale che hai inizializzato String.Empty, non l'istanza appena allocata creata e restituita da Marshal.PtrToStringUni().
mklement0

7

Ho creato i seguenti metodi di estensione in base alla risposta di rdev5 . Appuntare la stringa gestita è importante in quanto impedisce al garbage collector di spostarla e di lasciare dietro di sé copie che non è possibile cancellare.

Penso che il vantaggio della mia soluzione sia che non è necessario alcun codice non sicuro.

/// <summary>
/// Allows a decrypted secure string to be used whilst minimising the exposure of the
/// unencrypted string.
/// </summary>
/// <typeparam name="T">Generic type returned by Func delegate.</typeparam>
/// <param name="secureString">The string to decrypt.</param>
/// <param name="action">
/// Func delegate which will receive the decrypted password as a string object
/// </param>
/// <returns>Result of Func delegate</returns>
/// <remarks>
/// This method creates an empty managed string and pins it so that the garbage collector
/// cannot move it around and create copies. An unmanaged copy of the the secure string is
/// then created and copied into the managed string. The action is then called using the
/// managed string. Both the managed and unmanaged strings are then zeroed to erase their
/// contents. The managed string is unpinned so that the garbage collector can resume normal
/// behaviour and the unmanaged string is freed.
/// </remarks>
public static T UseDecryptedSecureString<T>(this SecureString secureString, Func<string, T> action)
{
    int length = secureString.Length;
    IntPtr sourceStringPointer = IntPtr.Zero;

    // Create an empty string of the correct size and pin it so that the GC can't move it around.
    string insecureString = new string('\0', length);
    var insecureStringHandler = GCHandle.Alloc(insecureString, GCHandleType.Pinned);

    IntPtr insecureStringPointer = insecureStringHandler.AddrOfPinnedObject();

    try
    {
        // Create an unmanaged copy of the secure string.
        sourceStringPointer = Marshal.SecureStringToBSTR(secureString);

        // Use the pointers to copy from the unmanaged to managed string.
        for (int i = 0; i < secureString.Length; i++)
        {
            short unicodeChar = Marshal.ReadInt16(sourceStringPointer, i * 2);
            Marshal.WriteInt16(insecureStringPointer, i * 2, unicodeChar);
        }

        return action(insecureString);
    }
    finally
    {
        // Zero the managed string so that the string is erased. Then unpin it to allow the
        // GC to take over.
        Marshal.Copy(new byte[length], 0, insecureStringPointer, length);
        insecureStringHandler.Free();

        // Zero and free the unmanaged string.
        Marshal.ZeroFreeBSTR(sourceStringPointer);
    }
}

/// <summary>
/// Allows a decrypted secure string to be used whilst minimising the exposure of the
/// unencrypted string.
/// </summary>
/// <param name="secureString">The string to decrypt.</param>
/// <param name="action">
/// Func delegate which will receive the decrypted password as a string object
/// </param>
/// <returns>Result of Func delegate</returns>
/// <remarks>
/// This method creates an empty managed string and pins it so that the garbage collector
/// cannot move it around and create copies. An unmanaged copy of the the secure string is
/// then created and copied into the managed string. The action is then called using the
/// managed string. Both the managed and unmanaged strings are then zeroed to erase their
/// contents. The managed string is unpinned so that the garbage collector can resume normal
/// behaviour and the unmanaged string is freed.
/// </remarks>
public static void UseDecryptedSecureString(this SecureString secureString, Action<string> action)
{
    UseDecryptedSecureString(secureString, (s) =>
    {
        action(s);
        return 0;
    });
}

Mentre il tuo codice non perde una copia della stringa, rappresenta comunque un abisso di disperazione . Quasi ogni operazione System.Stringsull'oggetto farà copie non accoppiate e non fotografate. Ecco perché questo non è integrato SecureString.
Ben Voigt,

Bello, anche se per azzerare l'intera stringa devi usare new char[length](o moltiplicare lengthcon sizeof(char)).
mklement0

@BenVoigt: finché action delegato non crea copie della stringa temporanea, bloccata e quindi azzerata, questo approccio dovrebbe essere sicuro o insicuro come SecureStringse stesso - per usare quest'ultima, anche una rappresentazione in testo semplice deve essere creato ad un certo punto, dato che le stringhe di sicurezza non sono costrutti a livello di sistema operativo; la sicurezza relativa deriva dal controllo della durata di quella stringa e dalla garanzia che venga cancellata dopo l'uso.
mklement0

@ mklement0: SecureStringnon ha funzioni membro e operatori sovraccarichi che eseguono copie ovunque . System.Stringlo fa.
Ben Voigt

1
@ mklement0: Che è davvero assurdo considerando che lo passa al NetworkCredential costruttore che accetta a SecureString.
Ben Voigt

0

Questo codice C # è quello che vuoi.

%ProjectPath%/SecureStringsEasy.cs

using System;
using System.Security;
using System.Runtime.InteropServices;
namespace SecureStringsEasy
{
    public static class MyExtensions
    {
        public static SecureString ToSecureString(string input)
        {
            SecureString secureString = new SecureString();
            foreach (var item in input)
            {
                secureString.AppendChar(item);
            }
            return secureString;
        }
        public static string ToNormalString(SecureString input)
        {
            IntPtr strptr = Marshal.SecureStringToBSTR(input);
            string normal = Marshal.PtrToStringBSTR(strptr);
            Marshal.ZeroFreeBSTR(strptr);
            return normal;
        }
    }
}

0

Derivo da questa risposta di sclarke81 . Mi piace la sua risposta e sto usando il derivato ma quello di sclarke81 ha un bug. Non ho reputazione quindi non posso commentare. Il problema sembra abbastanza piccolo da non giustificare un'altra risposta e potrei modificarlo. Così ho fatto. È stato respinto. Quindi ora abbiamo un'altra risposta.

sclarke81 Spero che tu veda questo (finalmente):

Marshal.Copy(new byte[length], 0, insecureStringPointer, length);

dovrebbe essere:

Marshal.Copy(new byte[length * 2], 0, insecureStringPointer, length * 2);

E la risposta completa con la correzione di bug:


    /// 
    /// Allows a decrypted secure string to be used whilst minimising the exposure of the
    /// unencrypted string.
    /// 
    /// Generic type returned by Func delegate.
    /// The string to decrypt.
    /// 
    /// Func delegate which will receive the decrypted password as a string object
    /// 
    /// Result of Func delegate
    /// 
    /// This method creates an empty managed string and pins it so that the garbage collector
    /// cannot move it around and create copies. An unmanaged copy of the the secure string is
    /// then created and copied into the managed string. The action is then called using the
    /// managed string. Both the managed and unmanaged strings are then zeroed to erase their
    /// contents. The managed string is unpinned so that the garbage collector can resume normal
    /// behaviour and the unmanaged string is freed.
    /// 
    public static T UseDecryptedSecureString(this SecureString secureString, Func action)
    {
        int length = secureString.Length;
        IntPtr sourceStringPointer = IntPtr.Zero;

        // Create an empty string of the correct size and pin it so that the GC can't move it around.
        string insecureString = new string('\0', length);
        var insecureStringHandler = GCHandle.Alloc(insecureString, GCHandleType.Pinned);

        IntPtr insecureStringPointer = insecureStringHandler.AddrOfPinnedObject();

        try
        {
            // Create an unmanaged copy of the secure string.
            sourceStringPointer = Marshal.SecureStringToBSTR(secureString);

            // Use the pointers to copy from the unmanaged to managed string.
            for (int i = 0; i < secureString.Length; i++)
            {
                short unicodeChar = Marshal.ReadInt16(sourceStringPointer, i * 2);
                Marshal.WriteInt16(insecureStringPointer, i * 2, unicodeChar);
            }

            return action(insecureString);
        }
        finally
        {
            // Zero the managed string so that the string is erased. Then unpin it to allow the
            // GC to take over.
            Marshal.Copy(new byte[length * 2], 0, insecureStringPointer, length * 2);
            insecureStringHandler.Free();

            // Zero and free the unmanaged string.
            Marshal.ZeroFreeBSTR(sourceStringPointer);
        }
    }

    /// 
    /// Allows a decrypted secure string to be used whilst minimising the exposure of the
    /// unencrypted string.
    /// 
    /// The string to decrypt.
    /// 
    /// Func delegate which will receive the decrypted password as a string object
    /// 
    /// Result of Func delegate
    /// 
    /// This method creates an empty managed string and pins it so that the garbage collector
    /// cannot move it around and create copies. An unmanaged copy of the the secure string is
    /// then created and copied into the managed string. The action is then called using the
    /// managed string. Both the managed and unmanaged strings are then zeroed to erase their
    /// contents. The managed string is unpinned so that the garbage collector can resume normal
    /// behaviour and the unmanaged string is freed.
    /// 
    public static void UseDecryptedSecureString(this SecureString secureString, Action action)
    {
        UseDecryptedSecureString(secureString, (s) =>
        {
            action(s);
            return 0;
        });
    }
}

Buon punto; Ho lasciato un commento sulla risposta referenziata, che dovrebbe informare l'OP.
mklement0

0

La soluzione di lavoro finale secondo la soluzione sclarke81 e le correzioni di John Flaherty è:

    public static class Utils
    {
        /// <remarks>
        /// This method creates an empty managed string and pins it so that the garbage collector
        /// cannot move it around and create copies. An unmanaged copy of the the secure string is
        /// then created and copied into the managed string. The action is then called using the
        /// managed string. Both the managed and unmanaged strings are then zeroed to erase their
        /// contents. The managed string is unpinned so that the garbage collector can resume normal
        /// behaviour and the unmanaged string is freed.
        /// </remarks>
        public static T UseDecryptedSecureString<T>(this SecureString secureString, Func<string, T> action)
        {
            int length = secureString.Length;
            IntPtr sourceStringPointer = IntPtr.Zero;

            // Create an empty string of the correct size and pin it so that the GC can't move it around.
            string insecureString = new string('\0', length);
            var insecureStringHandler = GCHandle.Alloc(insecureString, GCHandleType.Pinned);

            IntPtr insecureStringPointer = insecureStringHandler.AddrOfPinnedObject();

            try
            {
                // Create an unmanaged copy of the secure string.
                sourceStringPointer = Marshal.SecureStringToBSTR(secureString);

                // Use the pointers to copy from the unmanaged to managed string.
                for (int i = 0; i < secureString.Length; i++)
                {
                    short unicodeChar = Marshal.ReadInt16(sourceStringPointer, i * 2);
                    Marshal.WriteInt16(insecureStringPointer, i * 2, unicodeChar);
                }

                return action(insecureString);
            }
            finally
            {
                // Zero the managed string so that the string is erased. Then unpin it to allow the
                // GC to take over.
                Marshal.Copy(new byte[length * 2], 0, insecureStringPointer, length * 2);
                insecureStringHandler.Free();

                // Zero and free the unmanaged string.
                Marshal.ZeroFreeBSTR(sourceStringPointer);
            }
        }

        /// <summary>
        /// Allows a decrypted secure string to be used whilst minimising the exposure of the
        /// unencrypted string.
        /// </summary>
        /// <param name="secureString">The string to decrypt.</param>
        /// <param name="action">
        /// Func delegate which will receive the decrypted password as a string object
        /// </param>
        /// <returns>Result of Func delegate</returns>
        /// <remarks>
        /// This method creates an empty managed string and pins it so that the garbage collector
        /// cannot move it around and create copies. An unmanaged copy of the the secure string is
        /// then created and copied into the managed string. The action is then called using the
        /// managed string. Both the managed and unmanaged strings are then zeroed to erase their
        /// contents. The managed string is unpinned so that the garbage collector can resume normal
        /// behaviour and the unmanaged string is freed.
        /// </remarks>
        public static void UseDecryptedSecureString(this SecureString secureString, Action<string> action)
        {
            UseDecryptedSecureString(secureString, (s) =>
            {
                action(s);
                return 0;
            });
        }
    }

-5
// using so that Marshal doesn't have to be qualified
using System.Runtime.InteropServices;    
//using for SecureString
using System.Security;
public string DecodeSecureString (SecureString Convert) 
{
    //convert to IntPtr using Marshal
    IntPtr cvttmpst = Marshal.SecureStringToBSTR(Convert);
    //convert to string using Marshal
    string cvtPlainPassword = Marshal.PtrToStringAuto(cvttmpst);
    //return the now plain string
    return cvtPlainPassword;
}

Questa risposta ha una perdita di memoria.
Ben Voigt,

@BenVoigt Puoi spiegare ulteriormente per favore come questo ha una perdita di memoria?
El Ronnoco,

4
@ElRonnoco: Niente libera BSTResplicitamente e non è un oggetto .NET, quindi neanche il garbage collector se ne occupa. Confronta con stackoverflow.com/a/818709/103167 pubblicato 5 anni prima e non perde.
Ben Voigt,

Questa risposta non funziona su piattaforme non Windows. PtrToStringAuto è sbagliato per una spiegazione, vedere: github.com/PowerShell/PowerShell/issues/…
K. Frank

-5

Se si utilizza a StringBuilderanziché a string, al termine è possibile sovrascrivere il valore effettivo in memoria. In questo modo la password non rimarrà in memoria fino a quando la raccolta dei rifiuti non la raccoglie.

StringBuilder.Append(plainTextPassword);
StringBuilder.Clear();
// overwrite with reasonably random characters
StringBuilder.Append(New Guid().ToString());

2
Anche se questo è vero, il Garbage Collector potrebbe ancora spostare il buffer StringBuilder in memoria durante la compattazione generazionale, il che fa fallire il "sovrascrivere il valore reale", perché c'è un'altra (o più) copia rimanente che non viene distrutta.
Ben Voigt,

4
Questo non risponde nemmeno in remoto alla domanda.
Jay Sullivan,
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.