¿Cómo convertir SecureString a System.String?

156

Todas las reservas sobre su destrinca SecureString mediante la creación de un System.String salir de ella a un lado , ¿cómo se puede hacer esto?

¿Cómo puedo convertir un System.Security.SecureString ordinario en System.String?

Estoy seguro de que muchos de ustedes que están familiarizados con SecureString responderán que uno nunca debe transformar un SecureString en una cadena .NET ordinaria porque elimina todas las protecciones de seguridad. Yo sé . Pero en este momento mi programa hace todo con cadenas normales de todos modos, y yo estoy tratando de mejorar su seguridad y aunque voy a estar usando una API que devuelve un SecureString a mi Soy no tratando de utilizar eso para aumentar mi seguridad.

Soy consciente de Marshal.SecureStringToBSTR, pero no sé cómo tomar ese BSTR y hacer un System.String fuera de él.

Para aquellos que pueden exigir saber por qué querría hacer esto, bueno, estoy tomando una contraseña de un usuario y la envío como un formulario html POST para registrar al usuario en un sitio web. Entonces ... esto realmente tiene que hacerse con buffers administrados y sin cifrar. Si incluso pudiera obtener acceso al búfer no administrado y no cifrado, imagino que podría escribir byte por byte en la transmisión de la red y espero que eso mantenga la contraseña segura todo el tiempo. Espero una respuesta al menos a uno de estos escenarios.

Andrew Arnott
fuente

Respuestas:

192

Usa la System.Runtime.InteropServices.Marshalclase:

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

Si desea evitar crear un objeto de cadena administrado, puede acceder a los datos sin procesar mediante 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);
  }
}
Rasmus Faber
fuente
1
También obtuve mi voto positivo incluso años después, ¡gracias por la ayuda! Solo una nota rápida: esto también funciona como estático, en su propia memoria.
John Suit
Solía StopWatchy SecureStringToStringtomó 4.6sec para funcionar. Es lento para mí. ¿Alguien tiene el mismo tiempo o algo más rápido?
radbyx
@radbyx En una configuración de prueba rápida y sucia, puedo llamarlo 1000 veces en 76 ms. La primera invocación tarda 0.3 ms y las invocaciones posteriores ~ 0.07ms. ¿Qué tamaño tiene su cadena segura y qué versión del marco está utilizando?
Rasmus Faber
La longitud de mi secureString es 168. Estoy usando .NET Framework 3.5 si eso respondió a su pregunta. He tryed 5-10 veces está siempre alrededor de 4,5 a 4,65 seg ~ Me encanta conseguir su tiempo
radbyx
@RasmusFaber Lo malo es que agregué un Database.GetConnectionString()código en tu código para obtener mi secureString, que fue la parte malvada que tardó casi 5 segundos (¡y sí, debería investigarlo! :) Tu código tardó .00 mili segundos en mi cronómetro, así que es todo bien. Gracias por señalarme en la dirección correcta.
radbyx
108

Obviamente, usted sabe cómo esto invalida todo el propósito de un SecureString, pero lo repetiré de todos modos.

Si desea una línea, intente esto: (.NET 4 y superior solamente)

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

Donde securePassword es un SecureString.

Steve en CO
fuente
10
Si bien vence el propósito en la producción, su solución es perfecta para pruebas unitarias. Gracias.
beterthanlife
Esto me ayudó a descubrir que un SecureString (System.Security.SecureString) no se pasaba a mi ApiController (webapi). Thx
granadaCoder
55
Nota en PowerShell, esto es[System.Net.NetworkCredential]::new('', $securePassword).Password
stijn
1
@ TheIncorrigible1 puedes elaborar? Por ejemplo, ¿cuándo ''no es del mismo tipo que [String]::Empty? Tampoco New-Object Net.Credentialfunciona para mí: no se puede encontrar el tipo [Net.Credential]: verifique que el ensamblado que contiene este tipo esté cargado
desde el
2
Derrota el propósito de SecureString porque hace una copia no cifrada de su contenido de SecureString en una cadena normal. Cada vez que haces eso, estás agregando al menos una (y posiblemente con Garbage Collection) varias copias de tu cadena sin cifrar a la memoria. Esto se considera un riesgo para algunas aplicaciones sensibles a la seguridad y SecureString se implementó específicamente para reducir el riesgo.
Steve In CO
49

Dang justo después de publicar esto, encontré la respuesta profunda en este artículo . Pero si alguien sabe cómo acceder al búfer IntPtr no administrado y sin cifrar que expone este método, un byte a la vez para que no tenga que crear un objeto de cadena administrado para mantener mi seguridad alta, agregue una respuesta. :)

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

    try
    {
        return Marshal.PtrToStringBSTR(bstr);
    }
    finally
    {
        Marshal.FreeBSTR(bstr);
    }
}
Andrew Arnott
fuente
Ciertamente puede usar la unsafepalabra clave y a char*, solo llame bstr.ToPointer()y eche.
Ben Voigt
@BenVoigt BSTR tiene un terminador nulo después de los datos de la cadena por seguridad, pero también permite caracteres nulos incrustados en la cadena. Por lo tanto, es un poco más complicado que eso, también debe recuperar el prefijo de longitud que se encuentra antes de ese puntero. docs.microsoft.com/en-us/previous-versions/windows/desktop/…
Wim Coenen
@WimCoenen: Verdadero pero sin importancia. La longitud almacenada en el BSTR será una copia de la longitud ya disponible SecureString.Length.
Ben Voigt
@BenVoigt ah, mi mal. Pensé que SecureString no exponía ninguna información sobre la cadena.
Wim Coenen
@WimCoenen: SecureStringno está tratando de ocultar el valor, está tratando de evitar que se realicen copias del valor en regiones que no pueden sobrescribirse de manera confiable, como memoria recolectada de basura, archivos de paginación, etc. La intención es que cuando la SecureStringvida finalice, absolutamente no queda copia del secreto en la memoria. No le impide hacer y filtrar una copia, pero nunca lo hace.
Ben Voigt
15

En mi opinión, los métodos de extensión. son la forma más cómoda de resolver esto.

Tomé Steve en la excelente respuesta de CO y la puse en una clase de extensión de la siguiente manera, junto con un segundo método que agregué para admitir la otra dirección (cadena -> cadena segura) también, para que pueda crear una cadena segura y convertirla en una cadena normal después:

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 esto, ahora puede simplemente convertir sus cadenas de un lado a otro de la siguiente manera:

// 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

Pero tenga en cuenta que el método de decodificación solo debe usarse para realizar pruebas.

Mate
fuente
14

Creo que sería mejor encapsularSecureString funciones dependientes su lógica dependiente en una función anónima para un mejor control sobre la cadena descifrada en la memoria (una vez fijada).

La implementación para descifrar SecureStrings en este fragmento:

  1. Ancle la cadena en la memoria (que es lo que quiere hacer pero parece faltar en la mayoría de las respuestas aquí).
  2. Pase su referencia al delegado Func / Action.
  3. Frótelo de la memoria y suelte el GC en el finallybloque.

Obviamente, esto hace que sea mucho más fácil "estandarizar" y mantener a las personas que llaman en lugar de depender de alternativas menos deseables:

  • Devolver la cadena descifrada de una string DecryptSecureString(...)función auxiliar.
  • Duplicar este código donde sea necesario.

Observe aquí, tiene dos opciones:

  1. static T DecryptSecureString<T> que le permite acceder al resultado de la Func delegado desde la persona que llama (como se muestra en elDecryptSecureStringWithFunc método de prueba).
  2. static void DecryptSecureStringes simplemente una versión "nula" que emplea un Actiondelegado en casos en los que realmente no desea / necesita devolver nada (como se demuestra en el DecryptSecureStringWithActionmétodo de prueba).

Ejemplo de uso para ambos se puede encontrar en el StringsTest clase incluida.

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);
        }
    }
}

Obviamente, esto no evita el abuso de esta función de la siguiente manera, así que tenga cuidado de no hacer esto:

[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
}

¡Feliz codificación!

Matt Borja
fuente
¿Por qué no usar en Marshal.Copy(new byte[insecureString.Length], 0, insecureStringPointer, (int)insecureString.Length);lugar de la fixedsección?
sclarke81
@ sclarke81, buena idea, pero necesitarás usar [char], no [byte].
mklement0
1
El enfoque general es prometedor, pero no creo que su intento de anclar la cadena administrada que contiene la copia insegura (texto sin formato) sea efectivo: en cambio, lo que está anclando es el objeto de cadena original que ha inicializado String.Empty, no la instancia recién asignada creada y devuelta por Marshal.PtrToStringUni().
mklement0
7

Creé los siguientes métodos de extensión basados ​​en la respuesta de rdev5 . Fijar la cadena administrada es importante ya que evita que el recolector de basura la mueva y deje copias que no puede borrar.

Creo que la ventaja de mi solución es que no se necesita código inseguro.

/// <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;
    });
}
sclarke81
fuente
Si bien su código no pierde una copia de la cadena, todavía representa un pozo de desesperación . Casi todas las operaciones en el System.Stringobjeto harán copias sin fijar ni borrar. Es por eso que esto no está integrado SecureString.
Ben Voigt
Agradable, aunque para poner a cero toda la cadena tendrás que usar new char[length](o multiplicar lengthcon sizeof(char))
mklement0
@BenVoigt: siempre que el actiondelegado no cree copias de la cadena temporal, fijada y luego puesta a cero, este enfoque debe ser tan seguro o inseguro como él SecureStringmismo; para usar este último, una representación de texto sin formato también debe ser creado en algún momento, dado que las cadenas seguras no son construcciones de nivel OS; la seguridad relativa proviene de controlar la vida útil de esa cadena y garantizar que se borre después de su uso.
mklement0
@ mklement0: SecureStringno tiene funciones miembro y operadores sobrecargados que hacen copias en todo el lugar. System.Stringhace.
Ben Voigt
1
@ mklement0: lo cual es bastante absurdo teniendo en cuenta que lo pasa al NetworkCredentialconstructor que acepta a SecureString.
Ben Voigt
0

Este código C # es lo que quieres.

%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;
        }
    }
}
Eric Alexander Silveira
fuente
0

Derivé de esta respuesta por sclarke81 . Me gusta su respuesta y estoy usando la derivada, pero sclarke81 tiene un error. No tengo reputación, así que no puedo comentar. El problema parece lo suficientemente pequeño como para no garantizar otra respuesta y podría editarlo. Así que lo hice. Fue rechazado. Entonces ahora tenemos otra respuesta.

sclarke81 Espero que veas esto (finalmente):

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

debiera ser:

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

Y la respuesta completa con la corrección de errores:


    /// 
    /// 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;
        });
    }
}
John Flaherty
fuente
Buen punto; He dejado un comentario sobre la respuesta referenciada, que debería notificar al OP.
mklement0
0

La solución de trabajo final de acuerdo con la solución sclarke81 y las soluciones de John Flaherty es:

    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;
            });
        }
    }
Rustam Shafigullin
fuente
-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;
}
Jesse Motes
fuente
Esta respuesta tiene una pérdida de memoria.
Ben Voigt
@BenVoigt ¿Puede explicar más por favor cómo esto tiene una pérdida de memoria?
El Ronnoco
44
@ElRonnoco: Nada libera BSTRexplícitamente, y no es un objeto .NET, por lo que el recolector de basura tampoco se ocupa de ello. Compare con stackoverflow.com/a/818709/103167 que se publicó 5 años antes y no tiene fugas.
Ben Voigt
Esta respuesta no funciona en plataformas que no sean de Windows. PtrToStringAuto está equivocado para una explicación, ver: github.com/PowerShell/PowerShell/issues/…
K. Frank
-5

Si usa un en StringBuilderlugar de un string, puede sobrescribir el valor real en la memoria cuando haya terminado. De esa manera, la contraseña no se quedará en la memoria hasta que la recolección de basura la recoja.

StringBuilder.Append(plainTextPassword);
StringBuilder.Clear();
// overwrite with reasonably random characters
StringBuilder.Append(New Guid().ToString());
Michael Liben
fuente
2
Si bien esto es cierto, el recolector de basura aún puede mover el búfer StringBuilder en la memoria durante la compactación generacional, lo que hace que el "sobrescribir el valor real" falle, porque hay otra (o más) copia restante que no se destruye.
Ben Voigt
44
Esto ni siquiera responde remotamente la pregunta.
Jay Sullivan