Saya pikir itu akan menjadi yang terbaik untuk SecureString
fungsi dependen untuk merangkum logika dependen mereka dalam fungsi anonim untuk kontrol yang lebih baik atas string yang didekripsi dalam memori (setelah disematkan).
Implementasi untuk mendekripsi SecureStrings dalam cuplikan ini akan:
- Sematkan string dalam memori (yang ingin Anda lakukan tetapi tampaknya tidak ada sebagian besar jawaban di sini).
- Berikan referensi ke delegasi Func / Action.
- Gosok dari memori dan lepaskan GC di
finally
blok.
Ini jelas membuatnya jauh lebih mudah untuk "membakukan" dan mempertahankan penelepon vs mengandalkan alternatif yang kurang diinginkan:
- Mengembalikan string yang didekripsi dari a
string DecryptSecureString(...)
fungsi pembantu.
- Menggandakan kode ini dimanapun dibutuhkan.
Perhatikan di sini, Anda memiliki dua opsi:
static T DecryptSecureString<T>
yang memungkinkan Anda untuk mengakses hasil Func
delegasi dari pemanggil (seperti yang ditunjukkan dalam DecryptSecureStringWithFunc
metode pengujian).
static void DecryptSecureString
hanyalah versi "void" yang mempekerjakan Action
delegasi dalam kasus di mana Anda sebenarnya tidak ingin / perlu mengembalikan apa pun (seperti yang ditunjukkan dalam DecryptSecureStringWithAction
metode pengujian).
Contoh penggunaan untuk keduanya dapat ditemukan di StringsTest
kelas yang disertakan.
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);
}
}
}
Jelas, ini tidak mencegah penyalahgunaan fungsi ini dengan cara berikut, jadi berhati-hatilah untuk tidak melakukan ini:
[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
}
Selamat coding!