Creo que sería mejor encapsularSecureString
funciones dependientes su lógica dependiente en una función anónima para un mejor control sobre la cadena descifrada en la memoria (una vez fijada).
La implementación para descifrar SecureStrings en este fragmento:
- Ancle la cadena en la memoria (que es lo que quiere hacer pero parece faltar en la mayoría de las respuestas aquí).
- Pase su referencia al delegado Func / Action.
- Frótelo de la memoria y suelte el GC en el
finally
bloque.
Obviamente, esto hace que sea mucho más fácil "estandarizar" y mantener a las personas que llaman en lugar de depender de alternativas menos deseables:
- Devolver la cadena descifrada de una
string DecryptSecureString(...)
función auxiliar.
- Duplicar este código donde sea necesario.
Observe aquí, tiene dos opciones:
static T DecryptSecureString<T>
que le permite acceder al resultado de la Func
delegado desde la persona que llama (como se muestra en elDecryptSecureStringWithFunc
método de prueba).
static void DecryptSecureString
es simplemente una versión "nula" que emplea un Action
delegado en casos en los que realmente no desea / necesita devolver nada (como se demuestra en el DecryptSecureStringWithAction
método de prueba).
Ejemplo de uso para ambos se puede encontrar en el StringsTest
clase incluida.
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, esto no evita el abuso de esta función de la siguiente manera, así que tenga cuidado de no hacer esto:
[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 codificación!