Câu trả lời ngắn
tại sao tôi không thể nói:
SecureString password = new SecureString("password");
Bởi vì bây giờ bạn có password
trong bộ nhớ; không có cách nào để xóa sạch nó - đó chính xác là điểm của SecureString .
Câu trả lời dài
Lý do SecureString tồn tại là vì bạn không thể sử dụng ZeroMemory để xóa dữ liệu nhạy cảm khi bạn hoàn thành nó. Nó tồn tại để giải quyết một vấn đề tồn tại bởi vì CLR.
Trong một ứng dụng gốc thông thường, bạn sẽ gọi SecureZeroMemory
:
Lấp đầy một khối bộ nhớ với số không.
Lưu ý : SecureZeroMemory giống hệt ZeroMemory
, ngoại trừ trình biên dịch sẽ không tối ưu hóa nó đi.
Vấn đề là bạn không thể gọi ZeroMemory
hoặc SecureZeroMemory
bên trong .NET. Và trong chuỗi .NET là bất biến; bạn thậm chí không thể ghi đè lên nội dung của chuỗi như bạn có thể làm bằng các ngôn ngữ khác:
//Wipe out the password
for (int i=0; i<password.Length; i++)
password[i] = \0;
vậy, bạn có thể làm gì? Làm cách nào để chúng tôi cung cấp khả năng trong .NET để xóa mật khẩu hoặc số thẻ tín dụng khỏi bộ nhớ khi chúng tôi hoàn thành?
Cách duy nhất nó có thể được thực hiện sẽ được đặt chuỗi trong một số bản địa khối bộ nhớ, nơi bạn có thể sau đó gọi ZeroMemory
. Một đối tượng bộ nhớ riêng như:
- một BSTR
- một HGLOBAL
- Bộ nhớ không được quản lý CoTaskMem
SecureString cung cấp khả năng bị mất trở lại
Trong .NET, Chuỗi không thể bị xóa khi bạn hoàn thành với chúng:
- họ bất biến; bạn không thể ghi đè lên nội dung của họ
- bạn không thể
Dispose
trong số họ
- việc dọn dẹp của họ là sự thương xót của người thu gom rác
SecureString tồn tại như một cách để vượt qua sự an toàn của chuỗi và có thể đảm bảo việc dọn dẹp của chúng khi bạn cần.
Bạn đã hỏi câu hỏi:
tại sao tôi không thể nói:
SecureString password = new SecureString("password");
Bởi vì bây giờ bạn có password
trong bộ nhớ; không có cách nào để lau nó Nó bị kẹt ở đó cho đến khi CLR tình cờ quyết định sử dụng lại bộ nhớ đó. Bạn đã đưa chúng tôi trở lại nơi chúng tôi bắt đầu; một ứng dụng đang chạy với mật khẩu mà chúng ta không thể thoát khỏi và nơi kết xuất bộ nhớ (hoặc Trình theo dõi tiến trình) có thể nhìn thấy mật khẩu.
SecureString sử dụng API bảo vệ dữ liệu để lưu trữ chuỗi được mã hóa trong bộ nhớ; theo cách đó, chuỗi sẽ không tồn tại trong các tệp hoán đổi, bãi đổ vỡ hoặc thậm chí trong cửa sổ biến cục bộ với một đồng nghiệp đang xem qua bạn.
Làm thế nào để tôi đọc mật khẩu?
Sau đó là câu hỏi: làm thế nào để tôi tương tác với chuỗi? Bạn hoàn toàn không muốn một phương pháp như:
String connectionString = secureConnectionString.ToString()
bởi vì bây giờ bạn quay lại ngay nơi bạn bắt đầu - một mật khẩu bạn không thể thoát khỏi. Bạn muốn ép buộc nhà phát triển xử lý chuỗi nhạy cảm một cách chính xác - để nó có thể bị xóa khỏi bộ nhớ.
Đó là lý do tại sao .NET cung cấp ba hàm trợ giúp tiện dụng để sắp xếp SecureString vào bộ nhớ không được quản lý:
Bạn chuyển đổi chuỗi thành một bộ nhớ không được quản lý, xử lý nó và sau đó xóa nó một lần nữa.
Một số API chấp nhận SecureStrings . Ví dụ: trong ADO.net 4.5, SqlConnection.Credential lấy một bộ SqlCredential :
SqlCredential cred = new SqlCredential(userid, password); //password is SecureString
SqlConnection conn = new SqlConnection(connectionString);
conn.Credential = cred;
conn.Open();
Bạn cũng có thể thay đổi mật khẩu trong Chuỗi kết nối:
SqlConnection.ChangePassword(connectionString, cred, newPassword);
Và có rất nhiều vị trí bên trong .NET nơi họ tiếp tục chấp nhận Chuỗi đơn giản cho mục đích tương thích, sau đó nhanh chóng chuyển một vị trí đặt nó thành SecureString.
Làm cách nào để đưa văn bản vào SecureString?
Điều này vẫn để lại vấn đề:
Làm thế nào để tôi có được một mật khẩu vào SecureString ở nơi đầu tiên?
Đây là một thách thức, nhưng vấn đề là khiến bạn phải suy nghĩ về bảo mật.
Đôi khi các chức năng đã được cung cấp cho bạn. Ví dụ: điều khiển WPF PasswordBox có thể trả lại cho bạn mật khẩu đã nhập dưới dạng SecureString trực tiếp:
Nhận mật khẩu hiện đang được giữ bởi PasswordBox dưới dạng SecureString .
Điều này rất hữu ích vì ở mọi nơi bạn đã sử dụng để đi qua một chuỗi thô, giờ đây bạn có hệ thống loại phàn nàn rằng SecureString không tương thích với Chuỗi. Bạn muốn đi càng lâu càng tốt trước khi phải chuyển đổi SecureString thành chuỗi thông thường.
Chuyển đổi SecureString là đủ dễ dàng:
- SecureStringToBSTR
- PtrToStringBSTR
như trong:
private static string CreateString(SecureString secureString)
{
IntPtr intPtr = IntPtr.Zero;
if (secureString == null || secureString.Length == 0)
{
return string.Empty;
}
string result;
try
{
intPtr = Marshal.SecureStringToBSTR(secureString);
result = Marshal.PtrToStringBSTR(intPtr);
}
finally
{
if (intPtr != IntPtr.Zero)
{
Marshal.ZeroFreeBSTR(intPtr);
}
}
return result;
}
Họ thực sự không muốn bạn làm điều đó.
Nhưng làm thế nào để tôi có được một chuỗi vào SecureString? Vâng, điều bạn cần làm là dừng việc có mật khẩu trong Chuỗi ở vị trí đầu tiên. Bạn cần phải có nó trong một cái gì đó khác. Thậm chí là mộtChar[]
mảng sẽ hữu ích.
Đó là khi bạn có thể nối thêm từng ký tự và xóa bản rõ khi bạn hoàn thành:
for (int i=0; i < PasswordArray.Length; i++)
{
password.AppendChar(PasswordArray[i]);
PasswordArray[i] = (Char)0;
}
Bạn cần mật khẩu của bạn được lưu trữ trong một số bộ nhớ mà bạn có thể xóa. Tải nó vào SecureString từ đó.
tl; dr: SecureString tồn tại để cung cấp tương đương với ZeroMemory .
Một số người không nhìn thấy điểm xóa mật khẩu của người dùng khỏi bộ nhớ khi thiết bị bị khóa hoặc xóa sạch các lần nhấn phím khỏi bộ nhớ sau khi họ xác thực . Những người đó không sử dụng SecureString.
SecureString
phát triển mới: github.com/dotnet/pl platform