Como converter SecureString para System.String?

156

Todas as reservas sobre a falta de segurança do SecureString, criando um System.String, deixando de lado , como isso pode ser feito?

Como posso converter um System.Security.SecureString comum em System.String?

Tenho certeza de que muitos de vocês familiarizados com o SecureString responderão que nunca se deve transformar um SecureString em uma string .NET comum porque remove todas as proteções de segurança. Eu sei . Mas, no momento, meu programa faz tudo com seqüências comuns de qualquer maneira, e estou tentando aprimorar sua segurança e, embora eu esteja usando uma API que me devolva uma SecureString, não estou tentando usá-la para aumentar minha segurança.

Estou ciente do Marshal.SecureStringToBSTR, mas não sei como pegar esse BSTR e criar um System.String com ele.

Para aqueles que exigem saber por que eu desejaria fazer isso, estou pegando uma senha de um usuário e enviando-a como um formulário html POST para fazer logon do usuário em um site. Então ... isso realmente precisa ser feito com buffers gerenciados e não criptografados. Se eu conseguisse acessar o buffer não gerenciado e não criptografado, imagino que poderia escrever o fluxo de bytes por byte no fluxo da rede e espero que isso mantenha a senha segura por todo o caminho. Espero uma resposta para pelo menos um desses cenários.

Andrew Arnott
fonte

Respostas:

192

Use a 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 você deseja evitar a criação de um objeto de sequência gerenciada, é possível acessar os dados brutos usando 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
fonte
1
Recebi meu voto positivo mesmo anos depois, obrigado pela ajuda! Apenas uma observação rápida: isso também funciona como uma estática, em sua própria memória.
John Suit
Eu usei StopWatche SecureStringToStringlevei 4,6 segundos para executar. É lento para mim. Alguém consegue o mesmo tempo ou algo mais rápido?
Radbyx
@radbyx Em uma configuração de teste rápida e suja, posso chamá-lo 1000 vezes em 76ms. A primeira chamada leva 0,3 ms e as chamadas subseqüentes ~ 0,07ms. Qual é o tamanho da sua string segura e qual versão da estrutura você está usando?
Rasmus Faber
O comprimento do meu secureString é 168. Estou usando o .NET Framework 3.5 se isso respondeu à sua pergunta? Eu tentei 5-10 vezes é sempre em torno de 4,5-4,65 sec ~ Eu adoraria ter o seu tempo
radbyx
@RasmusFaber Meu mal, eu adicionei um Database.GetConnectionString()no seu código, para obter o meu secureString, que foi a parte maligna que levou quase 5 segundos (e sim, eu deveria analisar isso! :) Seu código levou 0,000 mili segundos no meu cronômetro, para que seja tudo bom. Obrigado por me apontar na direção certa.
radbyx
108

Obviamente, você sabe como isso derrota todo o propósito de um SecureString, mas eu o reafirmarei de qualquer maneira.

Se você deseja uma linha única, tente o seguinte: (somente .NET 4 e superior)

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

Onde securePassword é um SecureString.

Steve In CO
fonte
10
Embora derrote o objetivo na produção, sua solução é perfeita para testes de unidade. Obrigado.
beterthanlife
Isso me ajudou a descobrir que um SecureString (System.Security.SecureString) não estava sendo passado para o meu ApiController (webapi). Thx
granadaCoder
5
Nota no PowerShell, isto é #[System.Net.NetworkCredential]::new('', $securePassword).Password
stjn
1
@ TheIncorrigible1 você pode elaborar? Por exemplo, quando ''não é do mesmo tipo que [String]::Empty? Também New-Object Net.Credentialnão funciona para mim: Não é possível localizar tipo [Net.Credential]: verificar se o assembly que contém esse tipo é carregado
Stijn
2
Isso anula o objetivo de um SecureString, porque cria uma cópia não criptografada do seu conteúdo do SecureString para uma sequência normal. Toda vez que você faz isso, você está adicionando pelo menos uma (e com a Garbage Collection possivelmente várias) cópias da sua seqüência não criptografada na memória. Isso é considerado um risco para alguns aplicativos sensíveis à segurança e o SecureString foi implementado especificamente para reduzir o risco.
Steve Em CO
49

Dang. logo após postar isso, encontrei a resposta profunda neste artigo . Mas se alguém souber como acessar o buffer não gerenciado e não criptografado do IntPtr que esse método expõe, um byte de cada vez, para que eu não precise criar um objeto de string gerenciado para manter minha segurança alta, adicione uma resposta. :)

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

    try
    {
        return Marshal.PtrToStringBSTR(bstr);
    }
    finally
    {
        Marshal.FreeBSTR(bstr);
    }
}
Andrew Arnott
fonte
Você certamente pode usar a unsafepalavra - chave e a char*, basta ligar bstr.ToPointer()e transmitir.
Ben Voigt
@BenVoigt BSTR tem um terminador nulo após os dados da string por segurança, mas também permite caracteres nulos incorporados na string. Portanto, é um pouco mais complicado do que isso, você também precisa recuperar o prefixo de comprimento que fica diante desse ponteiro. docs.microsoft.com/pt-br/previous-versions/windows/desktop/…
Wim Coenen
@WimCoenen: Verdadeiro, mas sem importância. O comprimento armazenado no BSTR será uma cópia do comprimento já disponível em SecureString.Length.
Ben Voigt
@ BenVoigt ah, meu mal. Eu pensei que o SecureString não expunha nenhuma informação sobre a string.
Wm Coenen #
@WimCoenen: SecureStringnão está tentando ocultar o valor, está tentando impedir que cópias do valor sejam feitas em regiões que não podem ser sobrescritas com segurança, como memória coletada de lixo, arquivo de paginação etc. A intenção é que, quando a SecureStringvida útil terminar, absolutamente nenhuma cópia do segredo permanece na memória. Isso não impede que você faça e vaze uma cópia, mas nunca o faz.
Ben Voigt
15

Na minha opinião, métodos de extensão são a maneira mais confortável de resolver isso.

Peguei Steve na excelente resposta de CO e coloquei-a em uma classe de extensão da seguinte forma, juntamente com um segundo método que adicionei para apoiar a outra direção (string -> string segura), para que você possa criar uma string segura e convertê-la em uma sequência normal depois:

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

Com isso, agora você pode simplesmente converter suas seqüências para frente e para trás da seguinte maneira:

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

Mas lembre-se de que o método de decodificação deve ser usado apenas para teste.

Matt
fonte
14

Eu acho que seria melhor para SecureStringfunções dependentes encapsular sua lógica dependente em uma função anônima para melhor controle sobre a string descriptografada na memória (uma vez fixada).

A implementação para descriptografar SecureStrings neste trecho irá:

  1. Coloque a sequência na memória (que é o que você deseja fazer, mas parece estar faltando na maioria das respostas aqui).
  2. Passe sua referência ao delegado Func / Action.
  3. Limpe-o da memória e libere o GC no finallybloco.

Obviamente, isso torna muito mais fácil "padronizar" e manter os chamadores versus confiar em alternativas menos desejáveis:

  • Retornando a sequência descriptografada de uma string DecryptSecureString(...)função auxiliar.
  • Duplicar esse código sempre que necessário.

Observe aqui, você tem duas opções:

  1. static T DecryptSecureString<T>que permite acessar o resultado do Funcdelegado do chamador (conforme mostrado naDecryptSecureStringWithFunc método de teste).
  2. static void DecryptSecureStringé simplesmente uma versão "vazia" que emprega um Actiondelegado nos casos em que você realmente não deseja / precisa retornar nada (como demonstrado no DecryptSecureStringWithActionmétodo de teste).

Exemplo de uso para ambos pode ser encontrado na StringsTestclasse incluída.

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, isso não impede o abuso dessa função da seguinte maneira, portanto, tome cuidado para não fazer isso:

[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 codificação!

Matt Borja
fonte
Por que não usar em Marshal.Copy(new byte[insecureString.Length], 0, insecureStringPointer, (int)insecureString.Length);vez da fixedseção?
precisa saber é o seguinte
@ sclarke81, boa ideia, mas você precisará usar [char], não [byte].
mklement0 9/03
1
A abordagem geral é promissora, mas não acho que sua tentativa de fixar a string gerenciada que contém a cópia insegura (texto sem formatação) seja eficaz: o que você está fixando é o objeto original da string que inicializou String.Empty, não a instância recém-alocada criada e retornada por Marshal.PtrToStringUni().
mklement0 9/03
7

Eu criei os seguintes métodos de extensão com base na resposta do rdev5 . Fixar a sequência gerenciada é importante, pois evita que o coletor de lixo a mova e deixe para trás cópias que você não pode apagar.

Penso que a vantagem da minha solução é que não é necessário 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
fonte
Embora seu código não vaze uma cópia da string, ele ainda representa um poço de desespero . Quase todas as operações no System.Stringobjeto fazem cópias não fixadas e não utilizadas. É por isso que isso não está embutido SecureString.
Ben Voigt
Agradável, embora a zero fora toda a cadeia que vai ter que usar new char[length](ou se multiplicam lengthcom sizeof(char)).
mklement0 9/03
@BenVoigt: Desde que o actiondelegado não crie cópias da string temporária, fixada e zerada, essa abordagem deve ser tão segura quanto insegura como SecureStringela mesma - para usar a última, uma representação em texto sem formatação também deve ser ser criado em algum momento, considerando que cadeias seguras não são construções no nível do SO; a segurança relativa vem do controle do tempo de vida dessa sequência e da garantia de que ela seja apagada após o uso.
mklement0 9/03
@ mklement0: SecureStringnão possui funções membros e operadores sobrecarregados que fazem cópias em todo o lugar. System.Stringfaz.
Ben Voigt
1
@ mklement0: O que é um absurdo, considerando que ele é transmitido ao NetworkCredentialconstrutor que aceita a SecureString.
Ben Voigt
0

Esse código C # é o que você deseja.

%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
fonte
0

Eu derivado de Esta resposta por sclarke81 . Eu gosto da resposta dele e estou usando o derivado, mas o sclarke81's tem um bug. Eu não tenho reputação, então não posso comentar. O problema parece pequeno o suficiente para que não justifique outra resposta e eu possa editá-lo. Então eu fiz. Foi rejeitado. Então agora temos outra resposta.

Espero que você veja isso (finalmente):

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

deveria estar:

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

E a resposta completa com a correção de 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;
        });
    }
}
John Flaherty
fonte
Bom ponto; Deixei um comentário sobre a resposta mencionada, que deve notificar o OP.
mklement0 9/03
0

A solução final de trabalho de acordo com a solução sclarke81 e as correções de 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;
            });
        }
    }
Rustam Shafigullin
fonte
-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
fonte
Esta resposta tem um vazamento de memória.
Ben Voigt
@BenVoigt Você pode explicar mais, por favor, como isso tem um vazamento de memória?
El Ronnoco 21/06
4
@ElRonnoco: Nada libera o BSTRexplicitamente, e não é um objeto .NET, portanto o coletor de lixo também não cuida dele. Comparado a stackoverflow.com/a/818709/103167, publicado 5 anos antes e não vaza.
Ben Voigt
Esta resposta não funciona em plataformas não Windows. PtrToStringAuto está errado para uma explicação, consulte: github.com/PowerShell/PowerShell/issues/…
K. Frank
-5

Se você usar um em StringBuildervez de um string, poderá sobrescrever o valor real na memória quando terminar. Dessa forma, a senha não ficará na memória até que a coleta de lixo a recolha.

StringBuilder.Append(plainTextPassword);
StringBuilder.Clear();
// overwrite with reasonably random characters
StringBuilder.Append(New Guid().ToString());
Michael Liben
fonte
2
Enquanto isso é verdade, o coletor de lixo ainda pode mover o buffer StringBuilder na memória durante a compactação geracional, o que faz com que "sobrescreva o valor real" falhe, porque há outra (ou mais) cópia restante que não é destruída.
Ben Voigt
4
Isso nem sequer remotamente responde à pergunta.
Jay Sullivan