Comment convertir SecureString en System.String?

156

Toutes les réservations sur unsecuring votre SecureString en créant un System.String sur ce côté , comment peut - il faire?

Comment puis-je convertir un System.Security.SecureString ordinaire en System.String?

Je suis sûr que beaucoup d'entre vous qui connaissent SecureString répondront qu'il ne faut jamais transformer un SecureString en une chaîne .NET ordinaire car cela supprime toutes les protections de sécurité. Je sais . Mais pour le moment, mon programme fait tout avec des chaînes ordinaires de toute façon, et j'essaie d'améliorer sa sécurité et bien que je vais utiliser une API qui me renvoie un SecureString, je n'essaye pas de l'utiliser pour augmenter ma sécurité.

Je connais Marshal.SecureStringToBSTR, mais je ne sais pas comment prendre ce BSTR et en faire un System.String.

Pour ceux qui peuvent demander à savoir pourquoi je voudrais faire cela, eh bien, je prends un mot de passe d'un utilisateur et je le soumets sous forme de formulaire html POST pour connecter l'utilisateur à un site Web. Donc ... cela doit vraiment être fait avec des tampons gérés et non chiffrés. Si je pouvais même accéder au tampon non géré et non chiffré, j'imagine que je pourrais écrire un flux octet par octet sur le flux réseau et espérer que cela maintiendra le mot de passe sécurisé tout au long du trajet. J'espère une réponse à au moins un de ces scénarios.

Andrew Arnott
la source

Réponses:

192

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

Si vous souhaitez éviter de créer un objet chaîne géré, vous pouvez accéder aux données brutes en utilisant 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
la source
1
J'ai eu mon vote positif même des années plus tard, merci pour l'aide! Juste une petite note: cela fonctionne également comme un statique, dans sa propre mémoire.
John Suit
J'ai utilisé StopWatchet SecureStringToStringpris 4,6 secondes pour fonctionner. C'est trop lent pour moi. Quelqu'un at-il le même temps ou quelque chose de plus rapide?
radbyx
@radbyx Dans une configuration de test rapide et sale, je peux l'appeler 1000 fois en 76ms. Le premier appel prend 0,3 ms et les appels suivants ~ 0,07 ms. Quelle est la taille de votre chaîne sécurisée et quelle version du framework utilisez-vous?
Rasmus Faber
La longueur de ma chaîne secureString est de 168. J'utilise .NET Framework 3.5 si cela répond à votre question? J'ai essayé 5 à 10 fois, c'est toujours autour de 4,5-4,65 s ~ J'adorerais avoir votre temps
radbyx
@RasmusFaber Mon mauvais, j'avais ajouté un Database.GetConnectionString()dans votre code, pour obtenir mon secureString, qui était la partie perverse qui a pris presque 5sec (et oui je devrais regarder là-dedans! :) Votre code a pris .00 mili secondes dans mon chronomètre donc c'est tout bon. Merci de m'avoir pointé dans la bonne direction.
radbyx
108

De toute évidence, vous savez comment cela va à l'encontre de l'objectif général d'un SecureString, mais je le répéterai quand même.

Si vous voulez un one-liner, essayez ceci: (.NET 4 et supérieur uniquement)

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

Où securePassword est un SecureString.

Steve dans CO
la source
10
Même si elle va à l'encontre de l'objectif de la production, votre solution est parfaite pour les tests unitaires. Merci.
beterthanlife
Cela m'a aidé à comprendre qu'un SecureString (System.Security.SecureString) n'était pas transmis à mon ApiController (webapi). Thx
granadaCoder
5
Notez dans PowerShell que c'est[System.Net.NetworkCredential]::new('', $securePassword).Password
stijn
1
@ TheIncorrigible1 pouvez-vous élaborer? Par exemple, quand n'est ''pas le même type que [String]::Empty? Aussi New-Object Net.Credentialne fonctionne pas pour moi: Vous ne trouvez pas le type [Net.Credential]: vérifier que le contenant assemblage de ce type est chargé
stijn
2
Il va à l'encontre de l'objectif d'un SecureString car il crée une copie non chiffrée de votre contenu SecureString dans une chaîne normale. Chaque fois que vous faites cela, vous ajoutez au moins une (et éventuellement plusieurs copies avec Garbage Collection) de votre chaîne non chiffrée en mémoire. Ceci est considéré comme un risque pour certaines applications sensibles à la sécurité et SecureString a été implémenté spécifiquement pour réduire le risque.
Steve In CO
49

Dang. juste après avoir publié ceci, j'ai trouvé la réponse au fond de cet article . Mais si quelqu'un sait comment accéder au tampon non géré et non chiffré IntPtr que cette méthode expose, un octet à la fois afin que je n'ai pas à en créer un objet de chaîne gérée pour maintenir ma sécurité élevée, veuillez ajouter une réponse. :)

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

    try
    {
        return Marshal.PtrToStringBSTR(bstr);
    }
    finally
    {
        Marshal.FreeBSTR(bstr);
    }
}
Andrew Arnott
la source
Vous pouvez certainement utiliser le unsafemot - clé et a char*, il suffit d'appeler bstr.ToPointer()et de lancer.
Ben Voigt
@BenVoigt BSTR a un terminateur nul après les données de chaîne pour des raisons de sécurité, mais autorise également les caractères nuls incorporés dans la chaîne. C'est donc un peu plus compliqué que cela, vous devez également récupérer le préfixe de longueur qui se trouve avant ce pointeur.docs.microsoft.com/en-us/previous-versions/windows/desktop/…
Wim Coenen
@WimCoenen: Vrai mais sans importance. La longueur stockée dans le BSTR sera une copie de la longueur déjà disponible à partir de SecureString.Length.
Ben Voigt
@BenVoigt ah, mon mal. Je pensais que SecureString n'avait révélé aucune information sur la chaîne.
Wim Coenen
@WimCoenen: SecureStringn'essaie pas de masquer la valeur, il essaie d'empêcher que des copies de la valeur soient faites dans des régions qui ne peuvent pas être écrasées de manière fiable, telles que la mémoire récupérée, le fichier d'échange, etc.SecureString durée de vie se termine, absolument aucune copie du secret ne reste en mémoire. Cela ne vous empêche pas de faire et de divulguer une copie, mais cela ne le fait jamais.
Ben Voigt
15

À mon avis, les méthodes d'extension sont le moyen le plus confortable de résoudre ce problème.

J'ai pris Steve dans l' excellente réponse de CO et l' ai mise dans une classe d'extension comme suit, avec une deuxième méthode que j'ai ajoutée pour prendre en charge l'autre direction (chaîne -> chaîne sécurisée) également, afin que vous puissiez créer une chaîne sécurisée et la convertir en une chaîne normale aprè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;
    }
}

Avec cela, vous pouvez maintenant simplement convertir vos chaînes d'avant en arrière comme ceci:

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

Mais gardez à l'esprit que la méthode de décodage ne doit être utilisée que pour les tests.

Mat
la source
14

Je pense qu'il serait préférable que SecureStringles fonctions dépendantes encapsulent leur logique dépendante dans une fonction anonyme pour un meilleur contrôle sur la chaîne déchiffrée en mémoire (une fois épinglée).

La mise en œuvre pour déchiffrer SecureStrings dans cet extrait de code:

  1. Épinglez la chaîne en mémoire (ce que vous voulez faire mais qui semble manquer dans la plupart des réponses ici).
  2. Passer sa référence au délégué Func / Action.
  3. Nettoyez-le de la mémoire et libérez le GC dans le finallybloc.

Cela rend évidemment beaucoup plus facile de «standardiser» et de maintenir les appelants que de compter sur des alternatives moins souhaitables:

  • Renvoyer la chaîne déchiffrée à partir d'un string DecryptSecureString(...) fonction d'assistance.
  • Dupliquer ce code là où c'est nécessaire.

Remarquez ici, vous avez deux options:

  1. static T DecryptSecureString<T>qui vous permet d'accéder au résultat du Funcdélégué à partir de l'appelant (comme indiqué dans la DecryptSecureStringWithFuncméthode de test).
  2. static void DecryptSecureStringest simplement une version «vide» qui emploie un Actiondélégué dans les cas où vous ne voulez / devez rien renvoyer (comme démontré dans la DecryptSecureStringWithActionméthode de test).

Un exemple d'utilisation des deux peut être trouvé dans la StringsTestclasse incluse.

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

Évidemment, cela n'empêche pas l'utilisation abusive de cette fonction de la manière suivante, alors faites attention à ne pas le faire:

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

Bon codage!

Matt Borja
la source
Pourquoi ne pas utiliser à la Marshal.Copy(new byte[insecureString.Length], 0, insecureStringPointer, (int)insecureString.Length);place de la fixedsection?
sclarke81
@ sclarke81, bonne idée, mais vous devrez utiliser [char], non [byte].
mklement0
1
L'approche globale est prometteuse, mais je ne pense pas que votre tentative d'épingler la chaîne gérée qui contient la copie non sécurisée (texte brut) soit efficace: ce que vous épinglez à la place est l' objet de chaîne d' origine sur lequel vous avez initialisé String.Empty, pas l'instance nouvellement allouée créée et renvoyée par Marshal.PtrToStringUni().
mklement0
7

J'ai créé les méthodes d'extension suivantes basées sur la réponse de rdev5 . L'épinglage de la chaîne gérée est important car cela empêche le garbage collector de la déplacer et de laisser des copies que vous ne pouvez pas effacer.

Je pense que l'avantage de ma solution est qu'aucun code dangereux n'est nécessaire.

/// <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
la source
Bien que votre code ne fuit pas une copie de la chaîne, cela représente toujours un gouffre de désespoir . Presque toutes les opérations sur l' System.Stringobjet produiront des copies non épinglées et non effacées. C'est pourquoi cela n'est pas intégré SecureString.
Ben Voigt
Bien, bien que pour mettre à zéro toute la chaîne, vous devrez utiliser new char[length](ou multiplier lengthavec sizeof(char)).
mklement0
@BenVoigt: Tant que le actiondélégué ne crée pas de copies de la chaîne temporaire, épinglée, puis remise à zéro, cette approche doit être aussi sûre ou dangereuse qu'elle- SecureStringmême - pour utiliser cette dernière, une représentation en texte brut doit également être créé à un moment donné, étant donné que les chaînes sécurisées ne sont pas des constructions au niveau du système d'exploitation; la sécurité relative vient du contrôle de la durée de vie de cette chaîne et de son effacement après utilisation.
mklement0
@ mklement0: SecureStringn'a pas de fonctions membres et d'opérateurs surchargés qui font des copies partout. System.StringEst-ce que.
Ben Voigt
1
@ mklement0: Ce qui est sacrément absurde étant donné qu'il le transmet au NetworkCredentialconstructeur qui accepte a SecureString.
Ben Voigt
0

Ce code C # est ce que vous voulez.

%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
la source
0

J'ai dérivé de cette réponse par sclarke81 . J'aime sa réponse et j'utilise le dérivé mais sclarke81 a un bug. Je n'ai pas de réputation donc je ne peux pas commenter. Le problème semble suffisamment petit pour ne pas justifier une autre réponse et je pourrais le modifier. Alors je l'ai fait. Il a été rejeté. Alors maintenant, nous avons une autre réponse.

sclarke81 J'espère que vous voyez ceci (enfin):

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

devrait être:

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

Et la réponse complète avec le correctif de bogue:


    /// 
    /// 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
la source
Bon point; J'ai laissé un commentaire sur la réponse référencée, qui devrait notifier l'OP.
mklement0
0

La solution de travail finale selon la solution sclarke81 et les correctifs de John Flaherty est:

    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
la source
-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
la source
Cette réponse a une fuite de mémoire.
Ben Voigt
@BenVoigt Pouvez-vous expliquer plus en détail comment cela a une fuite de mémoire?
El Ronnoco
4
@ElRonnoco: Rien ne libère le BSTRexplicitement, et ce n'est pas un objet .NET donc le garbage collector ne s'en charge pas non plus. Comparez à stackoverflow.com/a/818709/103167 qui a été publié 5 ans plus tôt et qui ne fuit pas.
Ben Voigt
Cette réponse ne fonctionne pas sur les plates-formes non Windows. PtrToStringAuto est faux pour une explication voir: github.com/PowerShell/PowerShell/issues
K. Frank
-5

Si vous utilisez a StringBuilderau lieu de a string, vous pouvez remplacer la valeur réelle en mémoire lorsque vous avez terminé. De cette façon, le mot de passe ne restera pas en mémoire tant que le garbage collection ne le récupérera pas.

StringBuilder.Append(plainTextPassword);
StringBuilder.Clear();
// overwrite with reasonably random characters
StringBuilder.Append(New Guid().ToString());
Michael Liben
la source
2
Bien que cela soit vrai, le garbage collector peut toujours déplacer le tampon StringBuilder en mémoire pendant le compactage générationnel, ce qui entraîne l'échec du «remplacement de la valeur réelle», car il existe une autre copie (ou plusieurs) restante qui n'est pas détruite.
Ben Voigt
4
Cela ne répond même pas à distance à la question.
Jay Sullivan