Penso che sarebbe meglio incapsulareSecureString
funzioni dipendenti loro logica dipendente in una funzione anonima per un migliore controllo sulla stringa decifrata in memoria (una volta bloccata).
L'implementazione per la decrittografia di SecureStrings in questo frammento:
- Pin la stringa in memoria (che è quello che vuoi fare ma sembra mancare dalla maggior parte delle risposte qui).
- Passare il riferimento al delegato Func / Action.
- Sfregalo dalla memoria e rilascia il GC nel
finally
blocco.
Questo ovviamente rende molto più facile "standardizzare" e mantenere i chiamanti rispetto al fatto di fare affidamento su alternative meno desiderabili:
- Restituisce la stringa decifrata da una
string DecryptSecureString(...)
funzione di supporto.
- Duplicazione di questo codice ovunque sia necessario.
Nota qui, hai due opzioni:
static T DecryptSecureString<T>
che consente di accedere al risultato del Func
delegato dal chiamante (come mostrato nel DecryptSecureStringWithFunc
metodo di prova).
static void DecryptSecureString
è semplicemente una versione "nulla" che impiega un Action
delegato nei casi in cui in realtà non si desidera / non è necessario restituire nulla (come dimostrato nel DecryptSecureStringWithAction
metodo di test).
Un esempio di utilizzo per entrambi può essere trovato nella StringsTest
classe inclusa.
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);
}
}
}
Ovviamente, ciò non impedisce l'abuso di questa funzione nel modo seguente, quindi fai attenzione a non farlo:
[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
}
Buona programmazione!