首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET 記憶體敏感資料保護:SecureString 與記憶體鎖定實戰

     編輯:關於.NET

String 的安全性隱憂

  • 不可變性:任何修改產生新實例,舊實例殘留記憶體等待 GC。
  • 駐留機制:相同字串可能共用記憶體,生命週期不可控。
  • 換頁風險:敏感資料可能被寫入 pagefile.sys 或 hiberfil.sys,離線攻擊面擴大。
敏感資料洩漏路徑

.NET 2.0+ 解決方案:SecureString

System.Security.SecureString 使用 DPAPI (Data Protection API) 在記憶體中加密儲存字元,支援:

  • 加密內容,非明文
  • 僅限 NT 核心作業系統
  • MakeReadOnly() 防止修改
  • 實作 IDisposable,主動釋放非受控記憶體

基本用法

// 建立
var securePwd = new SecureString();
foreach (char c in "P@ssw0rd") securePwd.AppendChar(c);
securePwd.MakeReadOnly();

// 使用時解密(需 Marshal 互操作)
IntPtr ptr = Marshal.SecureStringToBSTR(securePwd);
try
{
    string plain = Marshal.PtrToStringBSTR(ptr);
    // 使用 plain 進行驗證、連線等
}
finally
{
    Marshal.ZeroFreeBSTR(ptr); // 必須釋放非受控記憶體
    securePwd.Dispose();
}

進階防禦:釘選記憶體防止換頁

解密後的字串仍為 Managed 物件,可能被 GC 移動或換頁。使用 GCHandle.Alloc(..., GCHandleType.Pinned) 釘選記憶體區塊:

public unsafe void UseSecureString(SecureString securePwd)
{
    int len = securePwd.Length;
    // 配置固定大小的字串緩衝區
    string buffer = new string('\0', len);
    GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
    IntPtr bstrPtr = IntPtr.Zero;

    try
    {
        bstrPtr = Marshal.SecureStringToBSTR(securePwd);
        char* src = (char*)bstrPtr;
        char* dst = (char*)handle.AddrOfPinnedObject();

        for (int i = 0; i < len; i++) dst[i] = src[i]; // 複製到釘選緩衝區

        // 此處使用 buffer 進行敏感操作
        // 例如:開啟資料庫連線、呼叫需明文密碼的 API
    }
    finally
    {
        if (bstrPtr != IntPtr.Zero) Marshal.ZeroFreeBSTR(bstrPtr);
        handle.Free(); // 釋放釘選
    }
}

極致防禦:VirtualLock 鎖定實體頁框

釘選僅防 GC 移動,無法阻止 OS 換頁。呼叫 Windows API VirtualLock 強制頁框常駐實體記憶體:

[DllImport("kernel32.dll")]
static extern bool VirtualLock(IntPtr lpAddress, UIntPtr dwSize);

[DllImport("kernel32.dll")]
static extern bool VirtualUnlock(IntPtr lpAddress, UIntPtr dwSize);

// 使用前
VirtualLock(handle.AddrOfPinnedObject(), (UIntPtr)buffer.Length * 2); // char = 2 bytes

// 使用後
VirtualUnlock(handle.AddrOfPinnedObject(), (UIntPtr)buffer.Length * 2);

⚠️ 警告:過度鎖定頁框會耗盡非分頁集區,導致系統效能驟降甚至死機。僅在極高安全需求(如金鑰管理模組)且短時間內使用。

現實困境與最佳實務

問題現況建議
使用者輸入來源TextBox/Console 回傳 String自訂控件直接寫入 SecureString,或輸入即轉換並清除原 String
API 相容性ADO.NET、LDAP、WebRequest 等需 String封裝輔助方法:SecureString → 釘選明文 → 呼叫 API → 立即清除
Framework 支援度部分 API 新增 SecureString 多載(如 X509Certificate2)優先使用支援 SecureString 的多載,避免中間明文

總結

  1. 棄用 String 儲存密碼/金鑰,全程使用 SecureString。
  2. 解密時使用釘選緩衝區,縮短明文暴露窗口。
  3. 謹慎使用 VirtualLock,權衡安全與系統穩定。
  4. 推動團隊建立「敏感資料處理規範」,代碼審查強制檢查。

.NET 提供了工具,關鍵在於開發者的安全意識與紀律。從 String 到 SecureString,是邁向記憶體安全的關鍵一步。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved