Como converter SecureString para System.String?

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

Como posso converter um System.Security.SecureString ordinário para System.String?

Tenho certeza de que muitos de vocês que estão familiarizados com o SecureString responderão que nunca se deve transformar um SecureString em uma string comum do .NET porque ele remove todas as proteções de segurança. Eu sei . Mas agora meu programa faz tudo com strings comuns de qualquer maneira, e eu estou tentando aumentar sua segurança e embora eu esteja usando uma API que retorne um SecureString para mim, eu não estou tentando usar isso para aumentar minha segurança.

Estou ciente de Marshal.SecureStringToBSTR, mas não sei como tirar esse BSTR e fazer um System.String fora dele.

Para aqueles que podem exigir saber por que eu iria querer fazer isso, bem, eu estou pegando uma senha de um usuário e enviando-a como um POST html para registrar o 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, eu poderia fazer um stream de byte por byte escrevendo no stream da rede e espero que isso mantenha a senha segura por todo o caminho. Eu estou esperando por uma resposta para pelo menos um desses cenários.

Use a class System.Runtime.InteropServices.Marshal :

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

Se você quiser evitar a criação de um object de string gerenciado, poderá 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); } } 

Obviamente, você sabe como isso derrota todo o propósito de um SecureString, mas vou reiterá-lo de qualquer maneira.

Se você quer um one-liner, tente isto: (.NET 4 e acima apenas)

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

Onde securePassword é um SecureString.

Dang logo após postar isso eu encontrei a resposta profunda neste artigo . Mas se alguém souber como acessar o buffer não gerenciado IntPtr não-criptografado que esse método expõe, um byte de cada vez, para que eu não precise criar um object 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); } } 

Eu acho que seria melhor para funções dependentes SecureString para encapsular sua lógica dependente em uma function anônima para melhor controle sobre a seqüência descriptografada na memory (uma vez fixada).

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

  1. Fixar a string na memory (que é o que você quer fazer, mas parece estar faltando na maioria das respostas aqui).
  2. Passe sua referência ao delegado Func / Action.
  3. Esfregue-o da memory e solte o GC no bloco finally .

Isso obviamente torna muito mais fácil “padronizar” e manter os chamadores em vez de confiar em alternativas menos desejáveis:

  • Retornando a string decriptografada de uma function auxiliar string DecryptSecureString(...) .
  • Duplicando este código onde for necessário.

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

  1. static T DecryptSecureString que permite acessar o resultado do delegado Func do chamador (como mostrado no método de teste DecryptSecureStringWithFunc ).
  2. static void DecryptSecureString é simplesmente uma versão “void” que emprega um delegado Action nos casos em que você realmente não deseja / precisa retornar nada (como demonstrado no método de teste DecryptSecureStringWithAction ).

O uso de exemplo para ambos pode ser encontrado na class StringsTest incluída.

Strings.cs

 using System; using System.Runtime.InteropServices; using System.Security; namespace SecurityUtils { public partial class Strings { ///  /// Passes decrypted password String pinned in memory to Func delegate scrubbed on return. ///  /// Generic type returned by Func delegate /// Func delegate which will receive the decrypted password pinned in memory as a String object /// Result of Func delegate public static T DecryptSecureString(SecureString secureString, Func 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 { insecureString = null; gcHandler.Free(); Marshal.ZeroFreeGlobalAllocUnicode(insecureStringPointer); } } ///  /// Runs DecryptSecureString with support for Action to leverage void return type ///  ///  ///  public static void DecryptSecureString(SecureString secureString, Action action) { DecryptSecureString(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(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 function 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 } 

Codificação feliz!

Na minha opinião, os methods de extensão são a maneira mais confortável de resolver isso.

Eu peguei Steve na excelente resposta do CO e coloquei em uma class de extensão como segue, junto com um segundo método que adicionei para suportar a outra direção (string -> string segura) também, então você pode criar uma string segura e convertê-la em uma corda 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 strings de um lado para outro da seguinte forma:

 // 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 tenha em mente que o método de decodificação só deve ser usado para testes.

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

Se você usar um StringBuilder vez de uma string , poderá sobrescrever o valor real na memory quando terminar. Dessa forma, a senha não ficará na memory até que a garbage collection a recolha.

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