ASP.NET Core 資料保護的非 DI 感知案例

作者:Rick Anderson

ASP.NET Core 資料保護系統通常會新增至服務容器,並透過相依性插入 (DI) 由相依元件取用。 不過,在某些情況下,這不可行或您不想這麼做,尤其是在將系統匯入現有應用程式時。

為了支援這些案例,Microsoft.AspNetCore.DataProtection.Extensions 套件提供具體型別 DataProtectionProvider,其提供簡單的方式來使用資料保護,而不需要依賴 DI。 例如,DataProtectionProvider 型別會實作 IDataProtectionProvider。 建構 DataProtectionProvider 只需要提供 DirectoryInfo 執行個體來指出應儲存提供者的密碼編譯金鑰的位置,如下列程式碼範例所示:

using System;
using System.IO;
using Microsoft.AspNetCore.DataProtection;

public class Program
{
    public static void Main(string[] args)
    {
        // Get the path to %LOCALAPPDATA%\myapp-keys
        var destFolder = Path.Combine(
            System.Environment.GetEnvironmentVariable("LOCALAPPDATA"),
            "myapp-keys");

        // Instantiate the data protection system at this folder
        var dataProtectionProvider = DataProtectionProvider.Create(
            new DirectoryInfo(destFolder));

        var protector = dataProtectionProvider.CreateProtector("Program.No-DI");
        Console.Write("Enter input: ");
        var input = Console.ReadLine();

        // Protect the payload
        var protectedPayload = protector.Protect(input);
        Console.WriteLine($"Protect returned: {protectedPayload}");

        // Unprotect the payload
        var unprotectedPayload = protector.Unprotect(protectedPayload);
        Console.WriteLine($"Unprotect returned: {unprotectedPayload}");

        Console.WriteLine();
        Console.WriteLine("Press any key...");
        Console.ReadKey();
    }
}

/*
 * SAMPLE OUTPUT
 *
 * Enter input: Hello world!
 * Protect returned: CfDJ8FWbAn6...ch3hAPm1NJA
 * Unprotect returned: Hello world!
 *
 * Press any key...
*/

根據預設,DataProtectionProvider 具體型別在將原始金鑰資料保存至檔案系統前,不會先進行加密。 這是為了支援開發人員指向網路共用,而資料保護系統無法自動推斷適當的待用金鑰加密機制的案例。

此外,DataProtectionProvider 具體型別預設不會隔離應用程式。 所有使用相同金鑰目錄的應用程式都可以共用承載,只要其目的參數相符即可。

DataProtectionProvider 建構函式接受選擇性的設定回呼,可用來調整系統的行為。 下列範例示範使用明確呼叫 SetApplicationName 來還原隔離。 此範例也會示範如何設定系統,以使用 Windows DPAPI 自動加密保存的金鑰。 如果目錄指向 UNC 共用,您可能會想要將共用憑證散發到所有相關電腦,並設定系統以使用憑證型加密搭配呼叫 ProtectKeysWithCertificate

using System;
using System.IO;
using Microsoft.AspNetCore.DataProtection;

public class Program
{
    public static void Main(string[] args)
    {
        // Get the path to %LOCALAPPDATA%\myapp-keys
        var destFolder = Path.Combine(
            System.Environment.GetEnvironmentVariable("LOCALAPPDATA"),
            "myapp-keys");

        // Instantiate the data protection system at this folder
        var dataProtectionProvider = DataProtectionProvider.Create(
            new DirectoryInfo(destFolder),
            configuration =>
            {
                configuration.SetApplicationName("my app name");
                configuration.ProtectKeysWithDpapi();
            });

        var protector = dataProtectionProvider.CreateProtector("Program.No-DI");
        Console.Write("Enter input: ");
        var input = Console.ReadLine();

        // Protect the payload
        var protectedPayload = protector.Protect(input);
        Console.WriteLine($"Protect returned: {protectedPayload}");

        // Unprotect the payload
        var unprotectedPayload = protector.Unprotect(protectedPayload);
        Console.WriteLine($"Unprotect returned: {unprotectedPayload}");

        Console.WriteLine();
        Console.WriteLine("Press any key...");
        Console.ReadKey();
    }
}

提示

DataProtectionProvider 具體型別的執行個體建立成本很高。 如果應用程式維護此型別的多個執行個體,而且它們全都使用相同的金鑰儲存體目錄,應用程式效能可能會降低。 如果您使用 DataProtectionProvider 型別,建議您建立此型別一次,並盡可能重複使用它。 從中建立的 DataProtectionProvider 型別和所有 IDataProtector 執行個體都是多個呼叫端的安全執行緒。