秘密管理員工具,預設會在專案的開發期間儲存機密資料,絕對不要將密碼或其他敏感性資料儲存在原始程式碼中,機密資料不應與應用程式一起部署。應該透過像是環境變數或 Azure 金鑰保存庫等受控方式來存取生產秘密。 您可以透過 Azure Key Vault 設定提供者 儲存及保護 Azure 測試與生產祕密。
開發環境
- Rider 2021.3.4
- Windows 10
- .Net Fx 4.8 via 新版專案範本 .NET Project SDKs
- Microsoft.Extensions.Configuration 5.0.0
- Microsoft.Extensions.Hosting 5.0.0
應用程式秘密組態
- 儲存在專案樹狀結構中的不同位置
- 會與特定專案建立關聯或在數個專案之間共用
- 不會簽入原始檔控制中。
秘密管理員工具不會加密儲存的密碼,也不應將其視為受信任的存放區。這僅適用于開發用途,金鑰和值會儲存在使用者設定檔目錄的 JSON 設定檔中。
設定秘密
專案目錄中執行下列命令
命令查詢
dotnet user-secrets -h
啟用秘密儲存體
dotnet user-secrets init
專案組態會多一個UserSecretsId的節點
<UserSecretsId>659be13b-676e-4c9e-a0b9-0df2ffd75cfc</UserSecretsId>
設定秘密
設定目前專案資料夾的秘密組態
dotnet user-secrets set "Player:Key" "from secret config 12345"
- "Player:Key" 是 Key
- "from secret config 12345" 是 Value
- Key-Value (string-string) ,用法很像 Configuration Key,請參考:如何使用組態 Microsoft.Extensions.Configuration | 余小章 @ 大內殿堂 - 點部落 (dotblogs.com.tw)
設定其他專案資料夾的秘密組態
dotnet user-secrets set "Player:Key" "from secret config 12345" --project "C:\apps\WebApp1\src\WebApp1"
最終存成秘密檔案時會壓扁結構
{
"Player:Key": "12345"
}
而不是像 josn 的檔案結構
{
"ConnectionStrings": {
"DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ConsoleApp.NewDb;Trusted_Connection=True;"
},
"Player": {
"AppId": "play1",
"Key": "1234567890"
}
}
}
檔案的路徑會在
Windows:%APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secrets.json
Linux/macOS:~/.microsoft/usersecrets/<user_secrets_id>/secrets.json
或是透過 VS IDE 開啟秘密檔案
移除秘密
dotnet user-secrets remove "Player:Key"
列出秘密
dotnet user-secrets list
移除所有秘密
dotnet user-secrets clear
讀取秘密
上篇,如何使用 .NET Generic Host for Microsoft.Extensions.Hosting | 余小章 @ 大內殿堂 - 點部落 (dotblogs.com.tw) 提到 Host.CreateDefaultBuilder 內部的行為已經幫我們載入秘密組態了
config.AddUserSecrets(appAssembly, optional: true);
Host.CreateDefaultBuilder 雖然會幫我們調用 IConfigurationBuilder.AddUserSecrets,在測試專案因條件不足
- IHostEnvironment.EnvironmentName == "Development "
- IHostEnvironment.ApplicationName ≠ null or string.empty
所以我要動一些手腳,修改 Host Configuration
[TestMethod]
public void 讀取秘密()
{
var builder = Host.CreateDefaultBuilder()
.ConfigureHostConfiguration((config)=>
{
config.AddJsonFile("appsettings.json", false, true);
})
;
var host = builder.Build();
var config = host.Services.GetService<IConfiguration>();
Console.WriteLine($"Player:Key = {config["Player:Key"]}");
}
在 appsettings.json增加 Environment、ApplicationName 節點
{
"ConnectionStrings": {
"DefaultConnectionString": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"
},
"Player": {
"AppId": "player1",
"Key": "1234567890"
},
"Environment": "Development",
"ApplicationName":"NetFx48"
}
執行結果可以讀取到秘密組態了
Player:Key = from secret config 12345
手動實例化組態並使用秘密
[TestMethod]
public void 手動實例化組態讀取秘密()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddUserSecrets<SurveyUserSecretTests>()
;
var config = builder.Build();
Console.WriteLine($"Player:Key = {config["Player:Key"]}");
}
參考
在 ASP.NET Core 的開發中安全儲存應用程式秘密 | Microsoft Docs
範例位置
sample.dotblog/SurveyUserSecretTests.cs at master · yaochangyu/sample.dotblog (github.com)
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET