Tôi nghĩ rằng nó là tốt nhất cho các SecureString
chức năng phụ thuộc để đóng gói logic phụ thuộc của chúng trong một hàm ẩn danh để kiểm soát tốt hơn chuỗi được giải mã trong bộ nhớ (một khi được ghim).
Việc triển khai để giải mã SecureStrings trong đoạn mã này sẽ:
- Ghim chuỗi trong bộ nhớ (đó là những gì bạn muốn làm nhưng dường như bị thiếu trong hầu hết các câu trả lời ở đây).
- Chuyển tham chiếu của nó cho đại biểu Func / Action.
- Chà nó khỏi bộ nhớ và giải phóng GC trong
finally
khối.
Điều này rõ ràng làm cho việc "chuẩn hóa" và duy trì người gọi dễ dàng hơn nhiều so với việc dựa vào các lựa chọn ít mong muốn hơn:
- Trả về chuỗi được giải mã từ
string DecryptSecureString(...)
hàm trợ giúp.
- Sao chép mã này bất cứ nơi nào cần thiết.
Lưu ý ở đây, bạn có hai lựa chọn:
static T DecryptSecureString<T>
cho phép bạn truy cập kết quả của Func
đại biểu từ người gọi (như thể hiện trong phầnDecryptSecureStringWithFunc
phương thức kiểm tra).
static void DecryptSecureString
chỉ đơn giản là một phiên bản "void" sử dụng một Action
đại biểu trong trường hợp bạn thực sự không muốn / cần phải trả lại bất cứ điều gì (như thể hiện trong DecryptSecureStringWithAction
phương pháp thử nghiệm).
Sử dụng ví dụ cho cả hai có thể được tìm thấy trong StringsTest
lớp bao gồm.
Chuỗi.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;
});
}
}
}
StringTest.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);
}
}
}
Rõ ràng, điều này không ngăn chặn việc lạm dụng chức năng này theo cách sau, vì vậy hãy cẩn thận không làm điều này:
[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
}
Chúc mừng mã hóa!