Note: see the link below for the English version of this article.

https://duongnt.com/securestring

Dùng SecureString trong C# để bảo mật dữ liệu

Đã có nhiều tài liệu viết về cách lưu mật khẩu. Nhưng bước đọc mật khẩu đó từ thiết bị đầu vào và lưu vào memory cũng không hề đơn giản. Đôi khi trong những project .NET Framework lâu đời, chúng ta gặp kiểu dữ liệu SecureString. Đây là một trong những cách đội phát triển framework dùng để bảo mật dữ liệu trong memory.

Vì sao ta không nên lưu bí mật bằng kiểu String?

Dưới đây là một chương trình đơn giản, nó đọc dữ liệu từ bàn phím và lưu vào một string.

private static void ReadString()
{
    Console.Write("Please enter your password: ");
    var password = Console.ReadLine();
    Console.WriteLine();
}

Lưu password bằng kiểu string

Tuy nhiên, khi dùng kiểu string để lưu bí mật như vậy, ta không thể kiểm soát được phần memory được cấp phát. Như đã biết, trong C# .NET kiểu string là reference type. Có nghĩa là memory của nó được cấp phát trên heap, và phần memory này sẽ được garbage collector quản lý. Ta không thể đảm bảo phần memory đó sẽ được giải phóng, và cũng không biết nó được giải phóng vào lúc nào.

Ta sẽ dump toàn bộ memory của chương trình trên bằng tool dotnet-dump rồi tìm thông tin bí mật trong dump đó bằng tool WinDbg. Dưới đây là lệnh để đọc toàn bộ string từ dump.

.foreach (address  {!DumpHeap -type System.String -short }) {du ${address}+c }

Khi chạy lệnh trên, ta thu được kết quả như sau.

Đọc nội dung string từ memory

Nếu ta chuyển sang dùng char array thì sau?

Liệu ta có thể đọc từng ký tự rồi lưu chúng vào một object StringBuilder được không? Lúc này ta sẽ không phải lo về string trong memory nữa, nhưng cách này có ổn không?

private static void ReadChars()
{
    Console.Write("Please enter your password: ");
    var sb = new StringBuilder();
    ConsoleKeyInfo key;
    do
    {
        key = Console.ReadKey();
        if (key.Key != ConsoleKey.Enter)
        {
            sb.Append(key.KeyChar);
        }
    }
    while (key.Key != ConsoleKey.Enter);
    Console.WriteLine();
}

Không may là cả trong trường nào, ta vẫn có thể đọc giá trị của array char từ dump. Đầu tiên, ta dùng lệnh dưới để lấy địa chỉ của tất cả array char trong dump.

!DumpHeap -type System.Char -short

Địa chỉ các array char

Rồi ta dùng lệnh dưới đây để đọc nội dung từng array.

db <địa chỉ>

Nội dung các char aray

Dù có khó đọc hơn một chút nhưng ta vẫn thấy được thông tin bí mật.

Đổi sang dùng SecureString

Khi sử dụng SecureString ta cần lưu ý không lưu bí mật trong bất kỳ biến kiểu string nào, kể cả biến tạm. Vì tất cả các biến string tạm đều được lưu trong memory và ta không kiểm soát được vòng đời của chúng. Đoạn code dưới đây được lấy từ trang web của Microsoft. Đây là một trong những cách đọc và lưu bí mật vào trong SecureString.

private static void SecureString()
{
    Console.Write("Please enter your password: ");
    using var securePassword = new SecureString();
    ConsoleKeyInfo key;
    do
    {
        key = Console.ReadKey();
        if (key.Key != ConsoleKey.Enter)
        {
            securePassword.AppendChar(key.KeyChar);
        }
    }
    while (key.Key != ConsoleKey.Enter);
    Console.WriteLine();
}

SecureString input

Ta lại tìm địa chỉ của các array char.

Địa chỉ các array char khi dùng SecureString

Sau đó ta sẽ xem nội dung của chúng.

SecureString char array content

Như đã thấy, không array char nào lưu thông tin bí mật của ta. Nhưng còn bản thân object SecureString thì sao? Ta sẽ tìm địa chỉ của nó trong dump.

!DumpHeap -type System.Security.SecureString -short

Địa chỉ object SecureString

Địa chỉ này cũng không lưu bí mật của ta.

Nội dung của object SecureString

Ta có nên dùng SecureString trong các project mới không?

Thực ra ta không nên dùng SecureString trong các project mới. Các bạn có thể tham khảo lý do chi tiết tại đây. Dưới đây là một vài nguyên nhân chính.

  • Mặc dù SecureString giúp giảm thiểu thời gian bí mật được lưu dưới dạng plain text trong memory, nó không giải quyết triệt để vấn đề này. Vì nói cho cùng .NET vẫn phải dùng cách nào đó để đọc bí mật từ thiết bị đầu vào.
  • SecureString dùng mã hóa để dữ cho bí mật không bị lộ. Nhưng không phải môi trường nào cũng hỗ trợ chức năng này.

Thay vào đó, ta nên sử dụng các phương pháp khác như certificate hay Windows authentication…

Kết thúc

Tôi biết đến SecureString nhờ đọc mã nguồn của một repository lâu đời. Ngày nay đây không còn là giải pháp tối ưu để giữ bí mật không bị lộ. Nhưng tôi vẫn thấy việc tìm hiểu memory dump của SecureString là tương đối thú vị.

A software developer from Vietnam and is currently living in Japan.

Leave a Reply