設定 ASP.NET Core 資料保護

當資料保護系統初始化時,其會根據操作環境套用預設設定。 這些設定適用於在單一機器上執行的應用程式。 不過,在某些情況下,開發人員可能會想要變更預設設定:

  • 應用程式會分散到多部機器。
  • 基於合規性考慮。

在這些案例中,資料保護系統提供豐富的組態 API。

警告

與組態檔類似,資料保護金鑰通道應使用適當的權限加以保護。 您可以選擇加密待用金鑰,但這不會阻止攻擊者建立新的金鑰。 因此,您的應用程式的安全性會受到影響。 使用資料保護所設定的儲存位置應受限於應用程式本身的存取,類似於您保護組態檔的方式。 例如,如果您選擇將金鑰通道儲存在磁碟上,請使用檔案系統權限。 請確定只有 Web 應用程式執行所在的身分識別具有該目錄的讀取、寫入和建立權限。 如果您使用 Azure Blob 儲存體,則只有 Web 應用程式才能夠在 Azure Blob 儲存體讀取、寫入或建立新項目等。

擴充方法 AddDataProtection 會傳回 IDataProtectionBuilderIDataProtectionBuilder 公開了擴充方法,您可以將其鏈結在一起以設定資料保護選項。

本文中使用的資料保護延伸模組需要下列 NuGet 封裝:

ProtectKeysWithAzureKeyVault

使用 CLI 登入 Azure,例如:

az login

若要使用 Azure Key Vault 管理金鑰,請在 Program.cs 中使用 ProtectKeysWithAzureKeyVault 設定系統。 blobUriWithSasToken 是儲存金鑰檔案的完整 URI。 URI 必須包含作為查詢字串參數的 SAS 權杖:

builder.Services.AddDataProtection()
    .PersistKeysToAzureBlobStorage(new Uri("<blobUriWithSasToken>"))
    .ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());

若要讓應用程式與 KeyVault 通訊並向其進行授權,必須新增 Azure.Identity 封裝。

設定金鑰通道儲存位置 (例如,PersistKeysToAzureBlobStorage)。 必須設定位置,因為呼叫 ProtectKeysWithAzureKeyVault 會實作停用自動資料保護設定的 IXmlEncryptor,包括金鑰通道儲存位置。 上述範例會使用 Azure Blob 儲存體來保存金鑰通道。 如需詳細資訊,請參閱金鑰儲存提供者:Azure 儲存體。 您也可以使用 PersistKeysToFileSystem 在本機保存金鑰通道。

keyIdentifier 是用於金鑰加密的金鑰保存庫金鑰識別碼。 例如, contosokeyvault 中名為 dataprotection 的金鑰保存庫中建立的金鑰具有金鑰識別碼 https://contosokeyvault.vault.azure.net/keys/dataprotection/。 為應用程式提供對金鑰保存庫的 Get取消換行金鑰,以及將金鑰解除包裝權限。

ProtectKeysWithAzureKeyVault 多載:

如果應用程式使用舊版 Azure 封裝 (Microsoft.AspNetCore.DataProtection.AzureStorage 和 Microsoft.AspNetCore.DataProtection.AzureKeyVault),建議您移除這些參考,並升級至 Azure.Extensions.AspNetCore.DataProtection.BlobsAzure.Extensions.AspNetCore.DataProtection.Keys。 這些封裝提供了新的更新,並解決舊封裝的一些重要安全性和穩定性問題。

builder.Services.AddDataProtection()
    // This blob must already exist before the application is run
    .PersistKeysToAzureBlobStorage("<storageAccountConnectionString", "<containerName>", "<blobName>")
    // Removing this line below for an initial run will ensure the file is created correctly
    .ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());

PersistKeysToFileSystem

若要將金鑰儲存在 UNC 共用上,而不是儲存在 %LOCALAPPDATA% 的預設位置,請使用 PersistKeysToFileSystem 設定系統:

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));

警告

如果您變更金鑰持續性位置,系統即不會再自動加密待用金鑰,因為其無法得知 DPAPI 是否為適當的加密機制。

PersistKeysToDbContext

若要使用 EntityFramework 將金鑰儲存在資料庫中,請使用 Microsoft.AspNetCore.DataProtection.EntityFrameworkCore 封裝來設定系統:

builder.Services.AddDataProtection()
    .PersistKeysToDbContext<SampleDbContext>();

上述程式碼會將金鑰儲存在設定的資料庫中。 所使用的資料庫內容必須實作 IDataProtectionKeyContextIDataProtectionKeyContext 會公開屬性 DataProtectionKeys

public DbSet<DataProtectionKey> DataProtectionKeys { get; set; } = null!;

這個屬性代表儲存索引鍵的資料表。 手動建立資料表,或使用 DbContext 移轉來建立資料表。 如需詳細資訊,請參閱DataProtectionKey

ProtectKeysWith*

您可以呼叫任何 ProtectKeysWith* 組態 API,來設定系統以保護金鑰待用。 請考慮下列範例,其會將金鑰儲存在 UNC 共用上,並使用特定的 X.509 憑證加密待用金鑰:

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
    .ProtectKeysWithCertificate(builder.Configuration["CertificateThumbprint"]);

您可以提供 X509Certificate2ProtectKeysWithCertificate,例如從檔案載入的憑證:

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
    .ProtectKeysWithCertificate(
        new X509Certificate2("certificate.pfx", builder.Configuration["CertificatePassword"]));

如需內建金鑰加密機制的更多範例和討論,請參閱待用金鑰加密

UnprotectKeysWithAnyCertificate

您可以透過將 X509Certificate2 憑證陣列與 UnprotectKeysWithAnyCertificate 結合使用,來輪替憑證和待用解密金鑰:

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
    .ProtectKeysWithCertificate(
        new X509Certificate2("certificate.pfx", builder.Configuration["CertificatePassword"]))
    .UnprotectKeysWithAnyCertificate(
        new X509Certificate2("certificate_1.pfx", builder.Configuration["CertificatePassword_1"]),
        new X509Certificate2("certificate_2.pfx", builder.Configuration["CertificatePassword_2"]));

SetDefaultKeyLifetime

若要將系統設定為使用 14 天而不是預設 90 天的金鑰存留期,請使用 SetDefaultKeyLifetime

builder.Services.AddDataProtection()
    .SetDefaultKeyLifetime(TimeSpan.FromDays(14));

SetApplicationName

根據預設,資料保護系統會根據應用程式的內容根目錄路徑相互隔離,即使它們共用相同的實體金鑰存放庫也是如此。 此隔離可防止應用程式理解彼此的受保護承載。

若要在應用程式之間共用受保護的承載:

builder.Services.AddDataProtection()
    .SetApplicationName("<sharedApplicationName>");

SetApplicationName 在內部設定了 DataProtectionOptions.ApplicationDiscriminator。 基於疑難排解目的,可以使用在 Program.cs 中構建 WebApplication 之後置的下列程式碼來紀錄架構指派至鑑別子的值:

var discriminator = app.Services.GetRequiredService<IOptions<DataProtectionOptions>>()
    .Value.ApplicationDiscriminator;
app.Logger.LogInformation("ApplicationDiscriminator: {ApplicationDiscriminator}", discriminator);

如需如何使用鑑別子的詳細資訊,請參閱本文後續的章節:

警告

在 .NET 6 中,WebApplicationBuilder 會將內容根路徑正規化,使其以 DirectorySeparatorChar 結尾。 例如,在 Windows 上,內容根路徑會以 \ 結尾,而在 Linux 以 / 結尾。 其他主機不會正規化路徑。 大部分從 HostBuilderWebHostBuilder 移轉的應用程式都不會共用相同的應用程式名稱,因為其不會以 DirectorySeparatorChar 終止。 若要解決此問題,請移除目錄分隔符號字元,並手動設定應用程式名稱,如下列程式碼所示:

using Microsoft.AspNetCore.DataProtection;
using System.Reflection;

var builder = WebApplication.CreateBuilder(args);
var trimmedContentRootPath = builder.Environment.ContentRootPath.TrimEnd(Path.DirectorySeparatorChar);
builder.Services.AddDataProtection()
 .SetApplicationName(trimmedContentRootPath);
var app = builder.Build();

app.MapGet("/", () => Assembly.GetEntryAssembly()!.GetName().Name);

app.Run();

DisableAutomaticKeyGeneration

您可能會遇到這樣的案例:不希望應用程式自動滾動金鑰(建立新金鑰),因為金鑰即將到期。 這種情況的一個案例可能是以主要/次要關係設定的應用程式,其中只有主應用程式才負責密鑰管理問題,而輔助應用程式僅具有密鑰通道的唯讀檢視。 藉由 DisableAutomaticKeyGeneration 設定系統,可將次要應用程式設定為將金鑰通道視為唯讀:

builder.Services.AddDataProtection()
    .DisableAutomaticKeyGeneration();

個別應用程式隔離

當資料保護系統由 ASP.NET Core 主機提供時,其會自動將應用程式相互隔離,即使這些應用程式是在相同的背景工作處理序帳戶下執行,且使用相同的主要金鑰資料也一樣。 這類似於 System.Web 的 <machineKey> 元素中之 IsolateApps 修飾元。

隔離機制的運作方式是將本機電腦上的每個應用程式視為唯一租用戶,因此任何指定應用程式的根目錄 IDataProtector 會自動將應用程式識別碼納入為鑑別子 (ApplicationDiscriminator)。 應用程式的唯一識別碼是應用程式的實體路徑:

  • 針對裝載於 IIS 的應用程式,唯一識別碼是應用程式的 IIS 實體路徑。 如果應用程式部署在 Web 服務器陣列環境中,則此值是穩定的 (假設 IIS 環境在 Web 服務器陣列中的所有機器上都以相似的方式進行設定)。
  • 對於在 Kestrel 伺服器上執行的自我裝載應用程式,唯一識別碼是磁碟上應用程式的實體路徑。

唯一識別碼的設計目的是要存留重設,無論是個別應用程式或是機器本身。

此隔離機制假設應用程式不具惡意。 惡意應用程式一律會影響在相同背景工作處理序帳戶下執行的任何其他應用程式。 在應用程式互不受信任的共用裝載環境中,裝載提供者應採取步驟確保應用程式之間的作業系統層級隔離,包括分隔應用程式的基礎金鑰存放庫。

如果 ASP.NET Core 主機未提供資料保護系統 (例如,如果您透過 DataProtectionProvider 具體類型具現化該保護系統),則根據預設,系統會停用應用程式隔離。 停用應用程式隔離時,只要應用程式提供適當的目的,則相同金鑰資料支援的所有應用程式都可以共用承載。 若要在此環境中提供應用程式隔離,請在組態物件上呼叫 SetApplicationName 方法,並為每個應用程式提供唯一的名稱。

資料保護和應用程式隔離

針對應用程式隔離,請考慮下列幾點:

  • 當多個應用程式指向相同的金鑰存放庫時,目的是讓這些應用程式共用相同的主要金鑰資料。 資料保護的開發假設是,所有共用金鑰通道的應用程式都可以存取該金鑰通道中的所有項目。 應用程式唯一識別碼可用來隔離衍生自金鑰通道所提供金鑰的應用程式特定金鑰。 其不需要項目層級權限,例如 Azure KeyVault 所提供用來強制執行額外的隔離的權限。 嘗試項目層級權限會產生應用程式錯誤。 如果您不想依賴內建應用程式隔離,則應該使用個別的金鑰存放區位置,而不使在應用程式之間共用。

  • 應用程式鑑別子 (ApplicationDiscriminator) 用來允許不同的應用程式共用相同的主要金鑰資料,但要讓其密碼編譯的承載彼此不同。 若要讓應用程式能夠讀取彼此的密碼編譯承載,其必須具有相同的應用程式鑑別子,這可以藉由呼叫 SetApplicationName 進行設定。

  • 如果應用程式遭到入侵 (例如,RCE 攻擊),則該應用程式可存取的所有主要金鑰資料也必須被視為遭到入侵,不論其待用保護狀態為何。 這表示,如果兩個應用程式指向相同的存放庫,即使它們使用不同的應用程式鑑別子,只要一個應用程式遭到入侵,在功能上皆等同於兩者遭到入侵。

    即使兩個應用程式使用不同的機制進行金鑰待用保護,這一「功能上等同於兩者的入侵」子句仍會保留。 一般而言,這不是預期的設定。 待用保護機制的目的是在敵人取得存放庫的讀取存取時提供保護。 取得存放庫寫入權限的敵人 (可能是因為其在應用程式內取得程式碼執行權限) 可向儲存體中插入惡意金鑰。 資料保護系統刻意不提供保護,以針對取得金鑰存放庫寫入存取的敵人進行防範。

  • 如果應用程式之間需要保持真正的隔離,則應使用不同的金鑰存放庫。 這自然脫離了「隔離」的定義。 如果應用程式具有彼此資料存放區的讀取和寫入存取,則不會進行隔離。

使用 UseCryptographicAlgorithms 變更演算法

資料保護堆疊可讓您變更新產生金鑰所使用的預設演算法。 若要這樣做,最簡單的方法是從組態回撥呼叫 UseCryptographicAlgorithms

builder.Services.AddDataProtection()
    .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration
    {
        EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
        ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
    });

預設 EncryptionAlgorithm 為 AES-256-CBC,而預設 ValidationAlgorithm 為 HMACSHA256。 預設原則可由系統管理員透過全機器原則設定,但針對 UseCryptographicAlgorithms 的明確呼叫會覆寫預設原則。

呼叫 UseCryptographicAlgorithms 可讓您從預先定義的內建清單中指定所需的演算法。 您不需要擔心演算法的實作。 在上述案例中,若是 Windows 上執行,則資料保護系統會嘗試使用 AES 的 CNG 實作。 否則,其會回復為受控 System.Security.Cryptography.Aes 類別。

您可以透過呼叫 UseCustomCryptographicAlgorithms 來手動指定實作。

提示

變更演算法不會影響金鑰通道中的現有金鑰。 其只會影響新產生的金鑰。

指定自訂受控演算法

若要指定自訂受控演算法,請建立指向實作類型的 ManagedAuthenticatedEncryptorConfiguration 執行個體:

builder.Services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(new ManagedAuthenticatedEncryptorConfiguration
    {
        // A type that subclasses SymmetricAlgorithm
        EncryptionAlgorithmType = typeof(Aes),

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // A type that subclasses KeyedHashAlgorithm
        ValidationAlgorithmType = typeof(HMACSHA256)
    });

一般而言,*Type 屬性必須指向 SymmetricAlgorithmKeyedHashAlgorithm 的具體、可具現化 (透過公用無參數的建構函式) 實作,不過為了方便起見,系統針對某些值 (例如,typeof(Aes)) 進行了特殊處理。

注意

SymmetricAlgorithm 的金鑰長度必須 ≥ 128 位元,區塊大小 ≥ 64 位元,且必須支援使用 PKCS #7 填補的 CBC 模式加密。 KeyedHashAlgorithm 的雜湊大小必須為 >= 128 位元,且必須支援長度等於雜湊演算法雜湊長度的金鑰。 KeyedHashAlgorithm 並非必須為 HMAC。

指定自訂 Windows CNG 演算法

若要使用 CBC 模式加密搭配 HMAC 驗證來指定自訂 Windows CNG 演算法,請建立包含演算法資訊的 CngCbcAuthenticatedEncryptorConfiguration 執行個體:

builder.Services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(new CngCbcAuthenticatedEncryptorConfiguration
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // Passed to BCryptOpenAlgorithmProvider
        HashAlgorithm = "SHA256",
        HashAlgorithmProvider = null
    });

注意

對稱區塊加密演算法的金鑰長度必須是 > = 128 位元、區塊大小 >= 64 位元,而且必須使用 PKCS #7 填補支援 CBC 模式加密。 雜湊演算法的摘要大小必須為 > = 128 位元,且必須支援以 BCRYPT_ALG_HANDLE_HMAC_FLAG 旗標開啟。 *Provider 屬性可以設定為 null,以針對指定的演算法使用預設提供者。 如需詳細資訊,請參閱 BCryptOpenAlgorithmProvider 文件。

若要使用 Galois/Counter 模式加密搭配驗證來指定自訂 Windows CNG 演算法,請建立包含演算法資訊的 CngGcmAuthenticatedEncryptorConfiguration 執行個體:

builder.Services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(new CngGcmAuthenticatedEncryptorConfiguration
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256
    });

注意

對稱區塊加密演算法的金鑰長度必須為 >= 128 位元、區塊大小正好為 128 位元,且必須支援 GCM 加密。 您可以將 EncryptionAlgorithmProvider 屬性設定為 null,以使用指定演算法的預設提供者。 如需詳細資訊,請參閱 BCryptOpenAlgorithmProvider 文件。

指定其他自訂演算法

雖然資料保護系統未作為第一級的 API,但其可擴展性足以允許指定幾乎任何類型的演算法。 例如,可以保留硬體安全模組 (HSM) 內含的所有金鑰,並提供核心加密和解密常式的自訂實作。 如需詳細資訊,請參閱 Core 密碼加密擴充性中的 IAuthenticatedEncryptor

在 Docker 容器中裝載時保存金鑰

Docker 容器中裝載時,應以下列其中一項中維護金鑰:

使用 Redis 保存金鑰

只應使用支援 Redis 資料持續性的 Redis 版本來儲存金鑰。 Azure Blob 儲存體是持續性的,可用來儲存金鑰。 如需詳細資訊,請參閱這個 GitHub 問題。

記錄 DataProtection

啟用 DataProtection 的 Information 層級記錄,以協助診斷問題。 下列 appsettings.json 檔案會啟用 DataProtection API 的資訊記錄:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.AspNetCore.DataProtection": "Information"
    }
  },
  "AllowedHosts": "*"
}

如需記錄的詳細資訊,請參閱 .NET Core 與 ASP.NET Core 中的記錄

其他資源

當資料保護系統初始化時,其會根據操作環境套用預設設定。 這些設定適用於在單一機器上執行的應用程式。 不過,在某些情況下,開發人員可能會想要變更預設設定:

  • 應用程式會分散到多部機器。
  • 基於合規性考慮。

在這些案例中,資料保護系統提供豐富的組態 API。

警告

與組態檔類似,資料保護金鑰通道應使用適當的權限加以保護。 您可以選擇加密待用金鑰,但這不會阻止攻擊者建立新的金鑰。 因此,您的應用程式的安全性會受到影響。 使用資料保護所設定的儲存位置應受限於應用程式本身的存取,類似於您保護組態檔的方式。 例如,如果您選擇將金鑰通道儲存在磁碟上,請使用檔案系統權限。 請確定只有 Web 應用程式執行所在的身分識別具有該目錄的讀取、寫入和建立權限。 如果您使用 Azure Blob 儲存體,則只有 Web 應用程式才能夠在 Azure Blob 儲存體讀取、寫入或建立新項目等。

擴充方法 AddDataProtection 會傳回 IDataProtectionBuilderIDataProtectionBuilder 公開了擴充方法,您可以將其鏈結在一起以設定資料保護選項。

本文中使用的資料保護延伸模組需要下列 NuGet 封裝:

ProtectKeysWithAzureKeyVault

使用 CLI 登入 Azure,例如:

az login

若要將金鑰儲存在 Azure Key Vault 中,請使用 Startup 類別中的 ProtectKeysWithAzureKeyVault 設定系統。 blobUriWithSasToken 是儲存金鑰檔案的完整 URI。 URI 必須包含作為查詢字串參數的 SAS 權杖:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToAzureBlobStorage(new Uri("<blobUriWithSasToken>"))
        .ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());
}

若要讓應用程式與 KeyVault 通訊並向其進行授權,必須新增 Azure.Identity 封裝。

設定金鑰通道儲存位置 (例如,PersistKeysToAzureBlobStorage)。 必須設定位置,因為呼叫 ProtectKeysWithAzureKeyVault 會實作停用自動資料保護設定的 IXmlEncryptor,包括金鑰通道儲存位置。 上述範例會使用 Azure Blob 儲存體來保存金鑰通道。 如需詳細資訊,請參閱金鑰儲存提供者:Azure 儲存體。 您也可以使用 PersistKeysToFileSystem 在本機保存金鑰通道。

keyIdentifier 是用於金鑰加密的金鑰保存庫金鑰識別碼。 例如, contosokeyvault 中名為 dataprotection 的金鑰保存庫中建立的金鑰具有金鑰識別碼 https://contosokeyvault.vault.azure.net/keys/dataprotection/。 為應用程式提供對金鑰保存庫的 Get取消換行金鑰,以及將金鑰解除包裝權限。

ProtectKeysWithAzureKeyVault 多載:

如果應用程式使用舊版 Azure 封裝 (Microsoft.AspNetCore.DataProtection.AzureStorage 和 Microsoft.AspNetCore.DataProtection.AzureKeyVault),建議您移除這些參考,並升級至 Azure.Extensions.AspNetCore.DataProtection.BlobsAzure.Extensions.AspNetCore.DataProtection.Keys。 這些封裝提供了新的更新,並解決舊封裝的一些重要安全性和穩定性問題。

services.AddDataProtection()
    //This blob must already exist before the application is run
    .PersistKeysToAzureBlobStorage("<storage account connection string", "<key store container name>", "<key store blob name>")
    //Removing this line below for an initial run will ensure the file is created correctly
    .ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());

PersistKeysToFileSystem

若要將金鑰儲存在 UNC 共用上,而不是儲存在 %LOCALAPPDATA% 的預設位置,請使用 PersistKeysToFileSystem 設定系統:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));
}

警告

如果您變更金鑰持續性位置,系統即不會再自動加密待用金鑰,因為其無法得知 DPAPI 是否為適當的加密機制。

PersistKeysToDbContext

若要使用 EntityFramework 將金鑰儲存在資料庫中,請使用 Microsoft.AspNetCore.DataProtection.EntityFrameworkCore 封裝來設定系統:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToDbContext<DbContext>()
}

上述程式碼會將金鑰儲存在設定的資料庫中。 所使用的資料庫內容必須實作 IDataProtectionKeyContextIDataProtectionKeyContext 會公開屬性 DataProtectionKeys

public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }

這個屬性代表儲存索引鍵的資料表。 手動建立資料表,或使用 DbContext 移轉來建立資料表。 如需詳細資訊,請參閱DataProtectionKey

ProtectKeysWith*

您可以呼叫任何 ProtectKeysWith* 組態 API,來設定系統以保護金鑰待用。 請考慮下列範例,其會將金鑰儲存在 UNC 共用上,並使用特定的 X.509 憑證加密待用金鑰:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
        .ProtectKeysWithCertificate(Configuration["Thumbprint"]);
}

您可以提供 X509Certificate2ProtectKeysWithCertificate,例如從檔案載入的憑證:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
        .ProtectKeysWithCertificate(
            new X509Certificate2("certificate.pfx", Configuration["Thumbprint"]));
}

如需內建金鑰加密機制的更多範例和討論,請參閱待用金鑰加密

UnprotectKeysWithAnyCertificate

您可以透過將 X509Certificate2 憑證陣列與 UnprotectKeysWithAnyCertificate 結合使用,來輪替憑證和待用解密金鑰:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
        .ProtectKeysWithCertificate(
            new X509Certificate2("certificate.pfx", Configuration["MyPasswordKey"));
        .UnprotectKeysWithAnyCertificate(
            new X509Certificate2("certificate_old_1.pfx", Configuration["MyPasswordKey_1"]),
            new X509Certificate2("certificate_old_2.pfx", Configuration["MyPasswordKey_2"]));
}

SetDefaultKeyLifetime

若要將系統設定為使用 14 天而不是預設 90 天的金鑰存留期,請使用 SetDefaultKeyLifetime

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .SetDefaultKeyLifetime(TimeSpan.FromDays(14));
}

SetApplicationName

根據預設,資料保護系統會根據應用程式的內容根目錄路徑相互隔離,即使它們共用相同的實體金鑰存放庫也是如此。 此隔離可防止應用程式理解彼此的受保護承載。

若要在應用程式之間共用受保護的承載:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .SetApplicationName("shared app name");
}

SetApplicationName 在內部設定了 DataProtectionOptions.ApplicationDiscriminator。 如需如何使用鑑別子的詳細資訊,請參閱本文後續的章節:

DisableAutomaticKeyGeneration

您可能會遇到這樣的案例:不希望應用程式自動滾動金鑰(建立新金鑰),因為金鑰即將到期。 這種情況的一個案例可能是以主要/次要關係設定的應用程式,其中只有主應用程式才負責密鑰管理問題,而輔助應用程式僅具有密鑰通道的唯讀檢視。 藉由 DisableAutomaticKeyGeneration 設定系統,可將次要應用程式設定為將金鑰通道視為唯讀:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .DisableAutomaticKeyGeneration();
}

個別應用程式隔離

當資料保護系統由 ASP.NET Core 主機提供時,其會自動將應用程式相互隔離,即使這些應用程式是在相同的背景工作處理序帳戶下執行,且使用相同的主要金鑰資料也一樣。 這類似於 System.Web 的 <machineKey> 元素中之 IsolateApps 修飾元。

隔離機制的運作方式是將本機電腦上的每個應用程式視為唯一租用戶,因此任何指定應用程式的根目錄 IDataProtector 會自動將應用程式識別碼納入為鑑別子 (ApplicationDiscriminator)。 應用程式的唯一識別碼是應用程式的實體路徑:

  • 針對裝載於 IIS 的應用程式,唯一識別碼是應用程式的 IIS 實體路徑。 如果應用程式部署在 Web 服務器陣列環境中,則此值是穩定的 (假設 IIS 環境在 Web 服務器陣列中的所有機器上都以相似的方式進行設定)。
  • 對於在 Kestrel 伺服器上執行的自我裝載應用程式,唯一識別碼是磁碟上應用程式的實體路徑。

唯一識別碼的設計目的是要存留重設,無論是個別應用程式或是機器本身。

此隔離機制假設應用程式不具惡意。 惡意應用程式一律會影響在相同背景工作處理序帳戶下執行的任何其他應用程式。 在應用程式互不受信任的共用裝載環境中,裝載提供者應採取步驟確保應用程式之間的作業系統層級隔離,包括分隔應用程式的基礎金鑰存放庫。

如果 ASP.NET Core 主機未提供資料保護系統 (例如,如果您透過 DataProtectionProvider 具體類型具現化該保護系統),則根據預設,系統會停用應用程式隔離。 停用應用程式隔離時,只要應用程式提供適當的目的,則相同金鑰資料支援的所有應用程式都可以共用承載。 若要在此環境中提供應用程式隔離,請在組態物件上呼叫 SetApplicationName 方法,並為每個應用程式提供唯一的名稱。

資料保護和應用程式隔離

針對應用程式隔離,請考慮下列幾點:

  • 當多個應用程式指向相同的金鑰存放庫時,目的是讓這些應用程式共用相同的主要金鑰資料。 資料保護的開發假設是,所有共用金鑰通道的應用程式都可以存取該金鑰通道中的所有項目。 應用程式唯一識別碼可用來隔離衍生自金鑰通道所提供金鑰的應用程式特定金鑰。 其不需要項目層級權限,例如 Azure KeyVault 所提供用來強制執行額外的隔離的權限。 嘗試項目層級權限會產生應用程式錯誤。 如果您不想依賴內建應用程式隔離,則應該使用個別的金鑰存放區位置,而不使在應用程式之間共用。

  • 應用程式鑑別子 (ApplicationDiscriminator) 用來允許不同的應用程式共用相同的主要金鑰資料,但要讓其密碼編譯的承載彼此不同。 若要讓應用程式能夠讀取彼此的密碼編譯承載,其必須具有相同的應用程式鑑別子,這可以藉由呼叫 SetApplicationName 進行設定。

  • 如果應用程式遭到入侵 (例如,RCE 攻擊),則該應用程式可存取的所有主要金鑰資料也必須被視為遭到入侵,不論其待用保護狀態為何。 這表示,如果兩個應用程式指向相同的存放庫,即使它們使用不同的應用程式鑑別子,只要一個應用程式遭到入侵,在功能上皆等同於兩者遭到入侵。

    即使兩個應用程式使用不同的機制進行金鑰待用保護,這一「功能上等同於兩者的入侵」子句仍會保留。 一般而言,這不是預期的設定。 待用保護機制的目的是在敵人取得存放庫的讀取存取時提供保護。 取得存放庫寫入權限的敵人 (可能是因為其在應用程式內取得程式碼執行權限) 可向儲存體中插入惡意金鑰。 資料保護系統刻意不提供保護,以針對取得金鑰存放庫寫入存取的敵人進行防範。

  • 如果應用程式之間需要保持真正的隔離,則應使用不同的金鑰存放庫。 這自然脫離了「隔離」的定義。 如果應用程式具有彼此資料存放區的讀取和寫入存取,則不會進行隔離。

使用 UseCryptographicAlgorithms 變更演算法

資料保護堆疊可讓您變更新產生金鑰所使用的預設演算法。 若要這樣做,最簡單的方法是從組態回撥呼叫 UseCryptographicAlgorithms

services.AddDataProtection()
    .UseCryptographicAlgorithms(
        new AuthenticatedEncryptorConfiguration()
    {
        EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
        ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
    });

預設 EncryptionAlgorithm 為 AES-256-CBC,而預設 ValidationAlgorithm 為 HMACSHA256。 預設原則可由系統管理員透過全機器原則設定,但針對 UseCryptographicAlgorithms 的明確呼叫會覆寫預設原則。

呼叫 UseCryptographicAlgorithms 可讓您從預先定義的內建清單中指定所需的演算法。 您不需要擔心演算法的實作。 在上述案例中,若是 Windows 上執行,則資料保護系統會嘗試使用 AES 的 CNG 實作。 否則,其會回復為受控 System.Security.Cryptography.Aes 類別。

您可以透過呼叫 UseCustomCryptographicAlgorithms 來手動指定實作。

提示

變更演算法不會影響金鑰通道中的現有金鑰。 其只會影響新產生的金鑰。

指定自訂受控演算法

若要指定自訂受控演算法,請建立指向實作類型的 ManagedAuthenticatedEncryptorConfiguration 執行個體:

serviceCollection.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new ManagedAuthenticatedEncryptorConfiguration()
    {
        // A type that subclasses SymmetricAlgorithm
        EncryptionAlgorithmType = typeof(Aes),

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // A type that subclasses KeyedHashAlgorithm
        ValidationAlgorithmType = typeof(HMACSHA256)
    });

一般而言,*Type 屬性必須指向 SymmetricAlgorithmKeyedHashAlgorithm 的具體、可具現化 (透過公用無參數的建構函式) 實作,不過為了方便起見,系統針對某些值 (例如,typeof(Aes)) 進行了特殊處理。

注意

SymmetricAlgorithm 的金鑰長度必須 ≥ 128 位元,區塊大小 ≥ 64 位元,且必須支援使用 PKCS #7 填補的 CBC 模式加密。 KeyedHashAlgorithm 的雜湊大小必須為 >= 128 位元,且必須支援長度等於雜湊演算法雜湊長度的金鑰。 KeyedHashAlgorithm 並非必須為 HMAC。

指定自訂 Windows CNG 演算法

若要使用 CBC 模式加密搭配 HMAC 驗證來指定自訂 Windows CNG 演算法,請建立包含演算法資訊的 CngCbcAuthenticatedEncryptorConfiguration 執行個體:

services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new CngCbcAuthenticatedEncryptorConfiguration()
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // Passed to BCryptOpenAlgorithmProvider
        HashAlgorithm = "SHA256",
        HashAlgorithmProvider = null
    });

注意

對稱區塊加密演算法的金鑰長度必須是 > = 128 位元、區塊大小 >= 64 位元,而且必須使用 PKCS #7 填補支援 CBC 模式加密。 雜湊演算法的摘要大小必須為 > = 128 位元,且必須支援以 BCRYPT_ALG_HANDLE_HMAC_FLAG 旗標開啟。 *Provider 屬性可以設定為 null,以針對指定的演算法使用預設提供者。 如需詳細資訊,請參閱 BCryptOpenAlgorithmProvider 文件。

若要使用 Galois/Counter 模式加密搭配驗證來指定自訂 Windows CNG 演算法,請建立包含演算法資訊的 CngGcmAuthenticatedEncryptorConfiguration 執行個體:

services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new CngGcmAuthenticatedEncryptorConfiguration()
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256
    });

注意

對稱區塊加密演算法的金鑰長度必須為 >= 128 位元、區塊大小正好為 128 位元,且必須支援 GCM 加密。 您可以將 EncryptionAlgorithmProvider 屬性設定為 null,以使用指定演算法的預設提供者。 如需詳細資訊,請參閱 BCryptOpenAlgorithmProvider 文件。

指定其他自訂演算法

雖然資料保護系統未作為第一級的 API,但其可擴展性足以允許指定幾乎任何類型的演算法。 例如,可以保留硬體安全模組 (HSM) 內含的所有金鑰,並提供核心加密和解密常式的自訂實作。 如需詳細資訊,請參閱 Core 密碼加密擴充性中的 IAuthenticatedEncryptor

在 Docker 容器中裝載時保存金鑰

Docker 容器中裝載時,應以下列其中一項中維護金鑰:

使用 Redis 保存金鑰

只應使用支援 Redis 資料持續性的 Redis 版本來儲存金鑰。 Azure Blob 儲存體是持續性的,可用來儲存金鑰。 如需詳細資訊,請參閱這個 GitHub 問題。

記錄 DataProtection

啟用 DataProtection 的 Information 層級記錄,以協助診斷問題。 下列 appsettings.json 檔案會啟用 DataProtection API 的資訊記錄:

{
  "Logging": {
    "LogLevel": {
      "Microsoft.AspNetCore.DataProtection": "Information"
    }
  }
}

如需記錄的詳細資訊,請參閱 .NET Core 與 ASP.NET Core 中的記錄

其他資源