設定 ASP.NET Core 資料保護
當資料保護系統初始化時,其會根據操作環境套用預設設定。 這些設定適用於在單一機器上執行的應用程式。 不過,在某些情況下,開發人員可能會想要變更預設設定:
- 應用程式會分散到多部機器。
- 基於合規性考慮。
在這些案例中,資料保護系統提供豐富的組態 API。
警告
與組態檔類似,資料保護金鑰通道應使用適當的權限加以保護。 您可以選擇加密待用金鑰,但這不會阻止攻擊者建立新的金鑰。 因此,您的應用程式的安全性會受到影響。 使用資料保護所設定的儲存位置應受限於應用程式本身的存取,類似於您保護組態檔的方式。 例如,如果您選擇將金鑰通道儲存在磁碟上,請使用檔案系統權限。 請確定只有 Web 應用程式執行所在的身分識別具有該目錄的讀取、寫入和建立權限。 如果您使用 Azure Blob 儲存體,則只有 Web 應用程式才能夠在 Azure Blob 儲存體讀取、寫入或建立新項目等。
擴充方法 AddDataProtection 會傳回 IDataProtectionBuilder。 IDataProtectionBuilder
公開了擴充方法,您可以將其鏈結在一起以設定資料保護選項。
注意
本文是針對在 Docker 容器內執行的應用程式所撰寫。 在 Docker 容器中,應用程式一律具有相同的路徑,因此,相同的應用程式分辨器。 需要在多個環境中執行的應用程式 (例如本機和已部署),必須設定環境的預設應用程式分辨器。 在多個環境中執行應用程式已超出本文的範圍。
本文中使用的資料保護延伸模組需要下列 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
多載:
- ProtectKeysWithAzureKeyVault(IDataProtectionBuilder, Uri, TokenCredential) 允許使用 keyIdentifier URI 和 tokenCredential 來使資料保護系統使用金鑰保存庫。
- ProtectKeysWithAzureKeyVault(IDataProtectionBuilder, String, IKeyEncryptionKeyResolver) 允許使用 keyIdentifier 字串和 IKeyEncryptionKeyResolver,以讓資料保護系統能夠使用金鑰保存庫。
如果應用程式使用舊版 Azure 封裝 (Microsoft.AspNetCore.DataProtection.AzureStorage 和 Microsoft.AspNetCore.DataProtection.AzureKeyVault),建議您移除這些參考,並升級至 Azure.Extensions.AspNetCore.DataProtection.Blobs 和 Azure.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>();
上述程式碼會將金鑰儲存在設定的資料庫中。 所使用的資料庫內容必須實作 IDataProtectionKeyContext
。 IDataProtectionKeyContext
會公開屬性 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"]);
您可以提供 X509Certificate2 到 ProtectKeysWithCertificate,例如從檔案載入的憑證:
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
根據預設,資料保護系統會根據應用程式的內容根目錄路徑相互隔離,即使它們共用相同的實體金鑰存放庫也是如此。 此隔離可防止應用程式理解彼此的受保護承載。
若要在應用程式之間共用受保護的承載:
- 使用相同的值,在每個應用程式中設定 SetApplicationName。
- 跨應用程式使用相同的資料保護 API (DPAPI) 堆疊版本。 在應用程式的專案檔中執行下列任一項作業:
- 透過 Microsoft.AspNetCore.App 中繼封裝參考相同的共用架構版本。
- 參考相同的資料保護封裝版本。
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 以 /
結尾。 其他主機不會正規化路徑。 大部分從 HostBuilder 或 WebHostBuilder 移轉的應用程式都不會共用相同的應用程式名稱,因為其不會以 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 屬性必須指向 SymmetricAlgorithm 和 KeyedHashAlgorithm 的具體、可具現化 (透過公用無參數的建構函式) 實作,不過為了方便起見,系統針對某些值 (例如,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 容器中裝載時,應以下列其中一項中維護金鑰:
- 作為 Docker 磁碟區並超過容器存留期的文件夾,例如共享磁碟區或主機裝載的磁碟區。
- 外部提供者,例如 Azure Blob 儲存體 (如
ProtectKeysWithAzureKeyVault
區段所示) 或 Redis。
使用 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 會傳回 IDataProtectionBuilder。 IDataProtectionBuilder
公開了擴充方法,您可以將其鏈結在一起以設定資料保護選項。
本文中使用的資料保護延伸模組需要下列 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
多載:
- ProtectKeysWithAzureKeyVault(IDataProtectionBuilder, Uri, TokenCredential) 允許使用 keyIdentifier URI 和 tokenCredential 來使資料保護系統使用金鑰保存庫。
- ProtectKeysWithAzureKeyVault(IDataProtectionBuilder, String, IKeyEncryptionKeyResolver) 允許使用 keyIdentifier 字串和 IKeyEncryptionKeyResolver,以讓資料保護系統能夠使用金鑰保存庫。
如果應用程式使用舊版 Azure 封裝 (Microsoft.AspNetCore.DataProtection.AzureStorage 和 Microsoft.AspNetCore.DataProtection.AzureKeyVault),建議您移除這些參考,並升級至 Azure.Extensions.AspNetCore.DataProtection.Blobs 和 Azure.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>()
}
上述程式碼會將金鑰儲存在設定的資料庫中。 所使用的資料庫內容必須實作 IDataProtectionKeyContext
。 IDataProtectionKeyContext
會公開屬性 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"]);
}
您可以提供 X509Certificate2 到 ProtectKeysWithCertificate,例如從檔案載入的憑證:
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
根據預設,資料保護系統會根據應用程式的內容根目錄路徑相互隔離,即使它們共用相同的實體金鑰存放庫也是如此。 此隔離可防止應用程式理解彼此的受保護承載。
若要在應用程式之間共用受保護的承載:
- 使用相同的值,在每個應用程式中設定 SetApplicationName。
- 跨應用程式使用相同的資料保護 API (DPAPI) 堆疊版本。 在應用程式的專案檔中執行下列任一項作業:
- 透過 Microsoft.AspNetCore.App 中繼封裝參考相同的共用架構版本。
- 參考相同的資料保護封裝版本。
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 屬性必須指向 SymmetricAlgorithm 和 KeyedHashAlgorithm 的具體、可具現化 (透過公用無參數的建構函式) 實作,不過為了方便起見,系統針對某些值 (例如,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 容器中裝載時,應以下列其中一項中維護金鑰:
- 作為 Docker 磁碟區並超過容器存留期的文件夾,例如共享磁碟區或主機裝載的磁碟區。
- 外部提供者,例如 Azure Blob 儲存體 (如
ProtectKeysWithAzureKeyVault
區段所示) 或 Redis。
使用 Redis 保存金鑰
只應使用支援 Redis 資料持續性的 Redis 版本來儲存金鑰。 Azure Blob 儲存體是持續性的,可用來儲存金鑰。 如需詳細資訊,請參閱這個 GitHub 問題。
記錄 DataProtection
啟用 DataProtection 的 Information
層級記錄,以協助診斷問題。 下列 appsettings.json
檔案會啟用 DataProtection API 的資訊記錄:
{
"Logging": {
"LogLevel": {
"Microsoft.AspNetCore.DataProtection": "Information"
}
}
}
如需記錄的詳細資訊,請參閱 .NET Core 與 ASP.NET Core 中的記錄。
其他資源
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應