Je pense qu'il serait préférable que SecureString
les 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:
- Épinglez la chaîne en mémoire (ce que vous voulez faire mais qui semble manquer dans la plupart des réponses ici).
- Passer sa référence au délégué Func / Action.
- Nettoyez-le de la mémoire et libérez le GC dans le
finally
bloc.
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:
static T DecryptSecureString<T>
qui vous permet d'accéder au résultat du Func
délégué à partir de l'appelant (comme indiqué dans la DecryptSecureStringWithFunc
méthode de test).
static void DecryptSecureString
est simplement une version «vide» qui emploie un Action
délégué dans les cas où vous ne voulez / devez rien renvoyer (comme démontré dans la DecryptSecureStringWithAction
méthode de test).
Un exemple d'utilisation des deux peut être trouvé dans la StringsTest
classe 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!