[c#] Comment convertir SecureString en System.String?


Answers

Évidemment, vous savez comment cela va à l'encontre de l'objectif d'un SecureString, mais je vais le répéter quand même.

Si vous voulez un one-liner, essayez ceci: (.NET 4 et ci-dessus seulement)

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

Où securePassword est un SecureString.

Question

Toutes les réservations sur la sécurisation de votre SecureString en créant un System.String à part , comment cela peut-il être fait?

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

Je suis sûr que beaucoup d'entre vous qui sont familiers avec SecureString vont répondre que l'on ne devrait jamais transformer une chaîne SecureString en une chaîne ordinaire .NET car elle supprime toutes les protections de sécurité. Je sais . Mais maintenant, mon programme fait tout avec des chaînes ordinaires et j'essaye d'améliorer sa sécurité et bien que je vais utiliser une API qui me renvoie un SecureString, je ne cherche pas à l'utiliser pour augmenter ma sécurité.

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

Pour ceux qui peuvent exiger de savoir pourquoi je voudrais jamais faire cela, eh bien, je prends un mot de passe d'un utilisateur et le soumettre comme un formulaire POST html pour connecter l'utilisateur dans un site web. Donc ... cela doit vraiment être fait avec des tampons gérés, non cryptés. Si je pouvais même avoir accès au tampon non chiffré et non crypté, j'imagine que je pourrais faire un flux de données octet par octet sur le flux réseau et j'espère que cela sécurisera le mot de passe tout le long. J'espère obtenir une réponse à au moins un de ces scénarios.




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



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

L'implémentation pour le décryptage de SecureStrings dans cet extrait va:

  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. Passez sa référence au délégué Func / Action.
  3. Frottez-le de la mémoire et libérez le GC dans le bloc finally .

Cela rend évidemment beaucoup plus facile de «standardiser» et de maintenir les appelants plutôt que de s'appuyer sur des alternatives moins souhaitables:

  • Renvoyer la chaîne déchiffrée à partir d'une fonction auxiliaire string DecryptSecureString(...) .
  • 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 délégué Func de l'appelant (comme indiqué dans la méthode de test DecryptSecureStringWithFunc ).
  2. static void DecryptSecureString est simplement une version "void" qui emploie un délégué Action dans les cas où vous ne voulez pas / ne voulez rien retourner (comme démontré dans la méthode de test DecryptSecureStringWithAction ).

Exemple d'utilisation pour les deux peut être trouvé dans la classe StringsTest inclus.

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
            {
                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'abus de cette fonction de la manière suivante, alors faites juste 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
}

Codage heureux!






Links