Khi nào tôi cần SecureString trong .NET?


179

Tôi đang cố gắng tìm hiểu mục đích của SecureString của .NET. Từ MSDN:

Một thể hiện của lớp System.String là bất biến và, khi không còn cần thiết, không thể được lập trình theo lịch trình để thu gom rác; nghĩa là, thể hiện chỉ đọc sau khi nó được tạo và không thể dự đoán khi nào thể hiện sẽ bị xóa khỏi bộ nhớ máy tính. Do đó, nếu một đối tượng Chuỗi chứa thông tin nhạy cảm như mật khẩu, số thẻ tín dụng hoặc dữ liệu cá nhân, có nguy cơ thông tin có thể bị tiết lộ sau khi sử dụng vì ứng dụng của bạn không thể xóa dữ liệu khỏi bộ nhớ máy tính.

Đối tượng SecureString tương tự như đối tượng String ở chỗ nó có giá trị văn bản. Tuy nhiên, giá trị của đối tượng SecureString được mã hóa tự động, có thể được sửa đổi cho đến khi ứng dụng của bạn đánh dấu nó ở chế độ chỉ đọc và có thể bị xóa khỏi bộ nhớ máy tính bởi ứng dụng của bạn hoặc trình thu gom rác .NET Framework.

Giá trị của một thể hiện của SecureString được mã hóa tự động khi thể hiện được khởi tạo hoặc khi giá trị được sửa đổi. Ứng dụng của bạn có thể khiến cho cá thể trở nên bất biến và ngăn chặn sửa đổi thêm bằng cách gọi phương thức MakeReadOnly.

Là mã hóa tự động các khoản thanh toán lớn?

Và tại sao tôi không thể nói:

SecureString password = new SecureString("password");

thay vì

SecureString pass = new SecureString();
foreach (char c in "password".ToCharArray())
    pass.AppendChar(c);

Tôi thiếu khía cạnh nào của SecureString?


3
11 năm sau và MS không còn khuyến nghị SecureStringphát triển mới: github.com/dotnet/pl platform
Matt Thomas

Câu trả lời:


4

Tôi sẽ ngừng sử dụng SecureString. Có vẻ như các anh chàng PG đang bỏ hỗ trợ cho nó. Thậm chí có thể kéo nó trong tương lai - https://github.com/dotnet/apireview/tree/master/2015-07-14-securestring .

Chúng ta nên xóa mã hóa khỏi SecureString trên tất cả các nền tảng trong .NET Core - Chúng ta nên lỗi thời SecureString - Có lẽ chúng ta không nên để lộ SecureString trong .NET Core


1
Liên kết đã chết nhưng dường như vẫn tiếp tục: docs.microsoft.com/en-us/dotnet/api/iêu .. với một số hướng dẫn rất yếu về đường dẫn chuyển tiếp "không sử dụng thông tin đăng nhập" - github.com/dotnet/pl platform- compat / blob / master / docs / DE0001.md .. bạn không dám sử dụng mật khẩu để bảo vệ khóa riêng của chứng chỉ của mình chứ!
felickz

1
Sau 11 năm, câu trả lời này có vẻ là câu trả lời 'mới'. Các liên kết dường như đã cũ nhưng hướng dẫn từ MS là: Không nên sử dụng SecureString
Richard Morgan

109

Một số phần của khung hiện đang sử dụng SecureString :

Mục đích chính là để giảm bề mặt tấn công, thay vì loại bỏ nó. SecureStringsđược "ghim" trong RAM để Bộ thu gom rác sẽ không di chuyển xung quanh hoặc tạo bản sao của nó. Nó cũng đảm bảo rằng văn bản đơn giản sẽ không được ghi vào tệp Hoán đổi hoặc trong các bãi chứa cốt lõi. Mã hóa giống như mã hóa hơn và sẽ không ngăn chặn một hacker xác định, tuy nhiên, người có thể tìm thấy khóa đối xứng được sử dụng để mã hóa và giải mã nó.

Như những người khác đã nói, lý do bạn phải tạo ra một SecureString nhân vật là do lỗ hổng rõ ràng đầu tiên của việc làm khác: có lẽ bạn đã có giá trị bí mật như một chuỗi đơn giản, vậy vấn đề là gì?

SecureStringĐây là bước đầu tiên để giải quyết vấn đề Gà và Trứng, vì vậy, mặc dù hầu hết các kịch bản hiện tại đều yêu cầu chuyển đổi chúng thành chuỗi thông thường để sử dụng chúng, nhưng sự tồn tại của chúng trong khung hiện tại có nghĩa là hỗ trợ tốt hơn cho chúng trong tương lai - ít nhất là đến một điểm mà chương trình của bạn không phải là liên kết yếu.


2
Tôi chạy qua nó nhìn vào thuộc tính Mật khẩu của ProcessStartInfo; thậm chí không chú ý đến loại, tôi chỉ đặt nó thành một chuỗi thông thường cho đến khi trình biên dịch sủa vào tôi.
Richard Morgan

Bạn sẽ không dễ dàng tìm thấy khóa mã hóa đối xứng, vì SecureString dựa trên DPAPI, không lưu trữ chính xác một khóa trong văn bản gốc ...
AviD

1
Ngoài ra, đó không phải là vấn đề trứng gà, vì nó không phải là sự thay thế cho mã hóa trong lưu trữ - mà là một cách giải quyết cho các chuỗi .NET được quản lý bất biến.
AviD

2
"bạn có lẽ đã có giá trị bí mật như một chuỗi đơn giản rồi, vậy vấn đề là gì?" Có câu trả lời cho câu hỏi này không? Có vẻ như tốt nhất đó là một giải pháp "tốt hơn không có gì" nếu bạn có ý định giữ mật khẩu được lưu trong bộ nhớ trong một thời gian dài.
xr280xr

2
Điều gì về việc có một vài ví dụ mã đơn giản của việc sử dụng? Tôi tin rằng tôi sẽ hiểu rõ hơn về cách thức và thời điểm sử dụng nó.
codea

37

Chỉnh sửa : Không sử dụng SecureString

Hướng dẫn hiện tại nói rằng lớp học không nên được sử dụng. Các chi tiết có thể được tìm thấy tại liên kết này: https://github.com/dotnet/pl platform-compat/blob/master/docs/DE0001.md

Từ bài viết:

DE0001: SecureString không nên được sử dụng

Động lực

  • Mục đích của SecureStringviệc tránh các bí mật được lưu trữ trong bộ nhớ tiến trình dưới dạng văn bản thuần túy.
  • Tuy nhiên, ngay cả trên Windows, SecureStringkhông tồn tại như một khái niệm hệ điều hành.
    • Nó chỉ làm cho cửa sổ nhận được văn bản đơn giản ngắn hơn; nó không hoàn toàn ngăn chặn nó vì .NET vẫn phải chuyển đổi chuỗi thành biểu diễn văn bản thuần túy.
    • Lợi ích là việc biểu diễn văn bản đơn giản không xuất hiện như một ví dụ của System.String- thời gian tồn tại của bộ đệm riêng ngắn hơn.
  • Nội dung của mảng không được mã hóa ngoại trừ trên .NET Framework.
    • Trong .NET Framework, nội dung của mảng char nội bộ được mã hóa. .NET không hỗ trợ mã hóa trong tất cả các môi trường, do thiếu API hoặc các vấn đề quản lý khóa.

sự giới thiệu

Đừng dùng SecureString mã mới. Khi chuyển mã sang .NET Core, hãy xem xét rằng nội dung của mảng không được mã hóa trong bộ nhớ.

Cách tiếp cận chung của việc xử lý thông tin đăng nhập là tránh chúng và thay vào đó dựa vào các phương tiện khác để xác thực, chẳng hạn như chứng chỉ hoặc xác thực Windows.

Kết thúc chỉnh sửa: Tóm tắt gốc bên dưới

Rất nhiều câu trả lời tuyệt vời; đây là một bản tóm tắt nhanh chóng về những gì đã được thảo luận.

Microsoft đã triển khai lớp SecureString trong nỗ lực cung cấp bảo mật tốt hơn với thông tin nhạy cảm (như thẻ tín dụng, mật khẩu, v.v.). Nó tự động cung cấp:

  • mã hóa (trong trường hợp bộ nhớ đệm hoặc bộ nhớ đệm trang)
  • ghim trong bộ nhớ
  • khả năng đánh dấu là chỉ đọc (để ngăn chặn mọi sửa đổi thêm)
  • xây dựng an toàn bằng cách KHÔNG cho phép một chuỗi liên tục được truyền vào

Hiện tại, SecureString bị hạn chế sử dụng nhưng dự kiến ​​sẽ áp dụng tốt hơn trong tương lai.

Dựa trên thông tin này, hàm tạo của SecureString không nên chỉ lấy một chuỗi và cắt nó thành mảng char vì chuỗi được đánh vần sẽ đánh bại mục đích của SecureString.

Thông tin bổ sung:

  • Một bài viết từ blog .NET Security nói về nhiều điều tương tự như được đề cập ở đây.
  • Và một một xem xét lại nó và nhắc đến một công cụ có thể đổ nội dung của SecureString.

Chỉnh sửa: Tôi thấy khó khăn để chọn câu trả lời tốt nhất vì có nhiều thông tin tốt; quá xấu không có tùy chọn trả lời hỗ trợ.


19

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ó passwordtrong 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 ZeroMemoryhoặc SecureZeroMemorybê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ó passwordtrong 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ó 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:

PasswordBox.SecurePassword Thuộc tính

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ự 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.


14

Có rất ít kịch bản mà bạn có thể sử dụng SecureString một cách hợp lý trong phiên bản hiện tại của Framework. Nó thực sự chỉ hữu ích khi tương tác với các API không được quản lý - bạn có thể sắp xếp nó bằng Marshal.SecureStringToGlobal ALLocUnicode.

Ngay khi bạn chuyển đổi nó thành / từ System.String, bạn đã đánh bại mục đích của nó.

Các MSDN mẫu tạo ra SecureString một nhân vật cùng một lúc từ giao diện điều khiển đầu vào và vượt qua chuỗi an toàn để một API không được quản lý. Nó khá phức tạp và không thực tế.

Bạn có thể mong đợi các phiên bản .NET trong tương lai sẽ có nhiều hỗ trợ hơn cho SecureString, điều này sẽ giúp nó hữu ích hơn, ví dụ:

  • Bảng điều khiển SecureString.ReadLineSecure () hoặc tương tự để đọc đầu vào bảng điều khiển vào SecureString mà không có tất cả các mã phức tạp trong mẫu.

  • WinForms TextBox thay thế lưu trữ thuộc tính TextBox.Text của nó dưới dạng một chuỗi bảo mật để mật khẩu có thể được nhập an toàn.

  • Tiện ích mở rộng cho các API liên quan đến bảo mật để cho phép mật khẩu được chuyển dưới dạng SecureString.

Nếu không có những điều trên, SecureString sẽ có giá trị giới hạn.


12

Tôi tin rằng lý do tại sao bạn phải thực hiện nối thêm ký tự thay vì một lần khởi tạo phẳng là vì trong nền chuyển "mật khẩu" cho hàm tạo của SecureString đặt chuỗi "mật khẩu" đó vào bộ nhớ đánh bại mục đích của chuỗi bảo mật.

Bằng cách nối thêm, bạn chỉ đưa một ký tự tại một thời điểm vào bộ nhớ, điều này dường như không liền kề với nhau về mặt vật lý khiến cho việc tái tạo chuỗi ban đầu trở nên khó khăn hơn nhiều. Tôi có thể sai ở đây nhưng đó là cách nó được giải thích với tôi.

Mục đích của lớp là ngăn chặn dữ liệu an toàn bị lộ thông qua kết xuất bộ nhớ hoặc công cụ tương tự.


11

MS nhận thấy rằng trong một số trường hợp khiến máy chủ (máy tính để bàn, bất cứ điều gì) bị sập, có những lúc môi trường thời gian chạy sẽ thực hiện kết xuất bộ nhớ để lộ nội dung của những gì trong bộ nhớ. Chuỗi bảo mật mã hóa nó trong bộ nhớ để ngăn kẻ tấn công có thể truy xuất nội dung của chuỗi.


5

Một trong những lợi ích lớn của SecureString là nó được cho là tránh khả năng dữ liệu của bạn được lưu trữ vào đĩa do bộ đệm ẩn trang. Nếu bạn có mật khẩu trong bộ nhớ và sau đó tải một chương trình hoặc tập dữ liệu lớn, mật khẩu của bạn có thể được ghi vào tệp hoán đổi khi chương trình của bạn bị xóa khỏi bộ nhớ. Với SecureString, ít nhất dữ liệu sẽ không được đặt xung quanh vô thời hạn trên đĩa của bạn dưới dạng văn bản rõ ràng.


4

Tôi đoán đó là vì chuỗi này có nghĩa là an toàn, tức là một hacker không thể đọc được nó. Nếu bạn khởi tạo nó bằng một chuỗi, tin tặc có thể đọc chuỗi gốc.


4

Vâng, như mô tả trạng thái, giá trị được lưu trữ được mã hóa, có nghĩa là kết xuất bộ nhớ của quy trình của bạn sẽ không tiết lộ giá trị của chuỗi (không có một số công việc khá nghiêm trọng).

Lý do bạn không thể xây dựng SecureString từ một chuỗi không đổi là vì sau đó bạn sẽ có một phiên bản chuỗi không được mã hóa trong bộ nhớ. Hạn chế bạn tạo chuỗi thành từng mảnh giúp giảm nguy cơ có toàn bộ chuỗi trong bộ nhớ cùng một lúc.


2
Nếu họ đang giới hạn việc xây dựng từ một chuỗi không đổi, thì dòng foreach (char c trong "password" .ToCharArray ()) sẽ đánh bại điều đó, phải không? Nó sẽ là pass.AppendChar ('p'); pass.AppendChar ('a'); v.v?
Richard Morgan

Có, bạn có thể dễ dàng vứt bỏ những gì bảo vệ nhỏ mà SecureString mang lại cho bạn. Họ đang cố gắng làm cho việc bắn vào chân mình thật khó khăn. Rõ ràng là phải có một số cách để đưa giá trị vào và ra khỏi SecureString, hoặc bạn không thể sử dụng nó cho bất cứ điều gì.
Mark Bessey

1

Một trường hợp sử dụng khác là khi bạn đang làm việc với các ứng dụng thanh toán (POS) và bạn chỉ đơn giản là không thể sử dụng các cấu trúc dữ liệu bất biến để lưu trữ dữ liệu nhạy cảm vì bạn là nhà phát triển cẩn thận. Ví dụ: nếu tôi sẽ lưu trữ dữ liệu thẻ nhạy cảm hoặc siêu dữ liệu ủy quyền thành chuỗi bất biến, sẽ luôn có trường hợp dữ liệu này sẽ có sẵn trong bộ nhớ trong một khoảng thời gian đáng kể sau khi bị loại bỏ. Tôi không thể đơn giản ghi đè lên nó. Một lợi thế lớn khác khi dữ liệu nhạy cảm như vậy được giữ trong bộ nhớ được mã hóa.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.