在 ASP.NET Core 的開發中安全儲存應用程式秘密
作者:Rick Anderson 與 Kirk Larkin
檢視或下載範例程式碼 \(英文\) (如何下載)
本檔說明如何在開發電腦上管理 ASP.NET Core 應用程式的敏感性資料。 永遠不要將密碼或其他敏感性資料儲存在原始程式碼中。 生產秘密不應該用於開發或測試。 不應使用應用程式部署秘密。 相反地,應該透過環境變數或 Azure 金鑰保存庫等受控制的方法來存取生產秘密。 您可以透過 Azure Key Vault 設定提供者儲存及保護 Azure 測試與生產祕密。
若要在 .NET 主控台應用程式中使用使用者密碼,請參閱 此 GitHub 問題 。
環境變數
環境變數可用來避免在程式碼或本機組態檔中儲存應用程式秘密。 環境變數會覆寫所有先前指定之組態來源的組態值。
請考慮啟用個別使用者帳戶 安全性的 ASP.NET Core Web 應用程式 。 預設資料庫連接字串包含在專案 appsettings.json
檔案中,且索引鍵 DefaultConnection
為 。 預設連接字串適用于 LocalDB,其會在使用者模式中執行,而且不需要密碼。 在應用程式部署期間, DefaultConnection
可以使用環境變數的值覆寫索引鍵值。 環境變數可能會使用敏感性認證來儲存完整的連接字串。
警告
環境變數通常會以純文字、未加密的文字儲存。 如果電腦或進程遭到入侵,則不受信任的合作物件可以存取環境變數。 可能需要額外的措施,以防止洩漏使用者秘密。
:
分隔符號不適用於所有平台上的環境變數階層式機碼。 __
,雙底線,為:
- 所有平台都支援。 例如,Bash 不支援
:
分隔符號,但支援__
。 - 自動由
:
取代
秘密管理員
秘密管理員工具會在開發 ASP.NET Core 專案時儲存敏感性資料。 在此內容中,機密資料片段是應用程式秘密。 應用程式秘密會儲存在專案樹狀目錄的個別位置。 應用程式秘密會與特定專案相關聯,或跨數個專案共用。 應用程式秘密不會簽入原始檔控制。
警告
秘密管理員工具不會加密儲存的秘密,不應被視為受信任的存放區。 僅供開發之用。 索引鍵和值會儲存在 JS 使用者設定檔目錄中的 ON 組態檔中。
秘密管理員工具的運作方式
秘密管理員工具會隱藏實作詳細資料,例如儲存值的位置和方式。 您可以在不知道這些實作詳細資料的情況下使用此工具。 這些值會儲存在 JS 本機電腦使用者設定檔資料夾中的 ON 檔案中:
檔案系統路徑:
%APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secrets.json
在上述檔案路徑中,將 取代 <user_secrets_id>
為 UserSecretsId
專案檔中指定的值。
請勿撰寫程式碼,此程式碼取決於使用秘密管理員工具儲存的資料位置或格式。 這些實作詳細資料可能會變更。 例如,秘密值不會加密,但可能在未來。
啟用秘密儲存體
Secret Manager 工具會針對儲存在使用者設定檔中的專案特定組態設定運作。
秘密管理員工具組含 init
命令。 若要使用使用者密碼,請在專案目錄中執行下列命令:
dotnet user-secrets init
上述命令會在專案檔的 內 PropertyGroup
加入 UserSecretsId
專案。 根據預設,的內部文字 UserSecretsId
是 GUID。 內部文字是任意的,但對專案而言是唯一的。
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UserSecretsId>79a3edd0-2092-40a2-a04d-dcb46d5ca9ed</UserSecretsId>
</PropertyGroup>
在 Visual Studio 中,以滑鼠右鍵按一下 方案總管 中的專案,然後從操作功能表中選取 [ 管理使用者密碼 ]。 此筆勢會將填入 GUID 的專案新增 UserSecretsId
至專案檔。
設定秘密
定義由金鑰及其值組成的應用程式秘密。 秘密與專案 UserSecretsId
的值相關聯。 例如,從專案檔所在的目錄執行下列命令:
dotnet user-secrets set "Movies:ServiceApiKey" "12345"
在上述範例中,冒號表示 Movies
是具有 ServiceApiKey
屬性的物件常值。
秘密管理員工具也可以從其他目錄使用。 --project
使用 選項來提供專案檔所在的檔案系統路徑。 例如:
dotnet user-secrets set "Movies:ServiceApiKey" "12345" --project "C:\apps\WebApp1\src\WebApp1"
JSVisual Studio 中的 ON 結構扁平化
Visual Studio 的 [管理使用者密碼] 手勢會在文字編輯器中開啟檔案 secrets.json
。 將 的內容 secrets.json
取代為要儲存的索引鍵/值組。 例如:
{
"Movies": {
"ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true",
"ServiceApiKey": "12345"
}
}
透過 或 dotnet user-secrets set
修改 JSdotnet user-secrets remove
之後,ON 結構會扁平化。 例如,執行 dotnet user-secrets remove "Movies:ConnectionString"
會 Movies
折迭物件常值。 修改過的檔案類似下列 JS ON:
{
"Movies:ServiceApiKey": "12345"
}
設定多個秘密
您可以透過將 ON 傳送 JS 至 set
命令,來設定一批秘密。 在下列範例中,檔案 input.json
的內容會透過管道傳送至 set
命令。
開啟命令殼層,然後執行下列命令:
type .\input.json | dotnet user-secrets set
存取秘密
若要存取秘密,請完成下列步驟:
註冊使用者秘密組態來源
使用者秘密設定 提供者 會向 .NET 組態 API 註冊適當的組態來源。
使用 dotnet new 或 Visual Studio 建立的 ASP.NET Core Web 應用程式會產生下列程式碼:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
WebApplication.CreateBuilder 會初始化具有預先設定之預設值的之 WebApplicationBuilder 類別的新執行個體。 初始化的 ( builder
) 會在 為 時 DevelopmentEnvironmentName 提供預設組態和呼叫 AddUserSecrets : WebApplicationBuilder
透過設定 API 讀取秘密
請考慮閱讀金鑰的 Movies:ServiceApiKey
下列範例:
Program.cs 檔案:
var builder = WebApplication.CreateBuilder(args);
var movieApiKey = builder.Configuration["Movies:ServiceApiKey"];
var app = builder.Build();
app.MapGet("/", () => movieApiKey);
app.Run();
Razor 頁面頁面模型:
public class IndexModel : PageModel
{
private readonly IConfiguration _config;
public IndexModel(IConfiguration config)
{
_config = config;
}
public void OnGet()
{
var moviesApiKey = _config["Movies:ServiceApiKey"];
// call Movies service with the API key
}
}
如需詳細資訊,請參閱 ASP.NET Core 中的組態。
將秘密對應至 POCO
將整個物件常值對應至 POCO(具有屬性的簡單 .NET 類別)對於匯總相關屬性很有用。
假設應用程式的 secrets.json
檔案包含下列兩個秘密:
{
"Movies:ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true",
"Movies:ServiceApiKey": "12345"
}
若要將上述秘密對應至 POCO,請使用 .NET 組態 API 的物件 圖形系結 功能。 下列程式碼系結至自訂 MovieSettings
POCO 並存取 ServiceApiKey
屬性值:
var moviesConfig =
Configuration.GetSection("Movies").Get<MovieSettings>();
_moviesApiKey = moviesConfig.ServiceApiKey;
Movies:ConnectionString
和 Movies:ServiceApiKey
秘密會對應至 中的 MovieSettings
個別屬性:
public class MovieSettings
{
public string ConnectionString { get; set; }
public string ServiceApiKey { get; set; }
}
使用秘密取代字串
將密碼儲存在純文字不安全。 例如,儲存在 中的 appsettings.json
資料庫連接字串可能包含指定使用者的密碼:
{
"ConnectionStrings": {
"Movies": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;User Id=johndoe;Password=pass123;MultipleActiveResultSets=true"
}
}
更安全的方法是將密碼儲存為秘密。 例如:
dotnet user-secrets set "DbPassword" "pass123"
Password
從 中的 appsettings.json
連接字串中移除機碼/值組。 例如:
{
"ConnectionStrings": {
"Movies": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;User Id=johndoe;MultipleActiveResultSets=true"
}
}
秘密的值可以在物件的 Password 屬性上 SqlConnectionStringBuilder 設定,以完成連接字串:
using System.Data.SqlClient;
var builder = WebApplication.CreateBuilder(args);
var conStrBuilder = new SqlConnectionStringBuilder(
builder.Configuration.GetConnectionString("Movies"));
conStrBuilder.Password = builder.Configuration["DbPassword"];
var connection = conStrBuilder.ConnectionString;
var app = builder.Build();
app.MapGet("/", () => connection);
app.Run();
列出秘密
假設應用程式的 secrets.json
檔案包含下列兩個秘密:
{
"Movies:ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true",
"Movies:ServiceApiKey": "12345"
}
從專案檔所在的目錄中執行下列命令:
dotnet user-secrets list
會出現下列輸出:
Movies:ConnectionString = Server=(localdb)\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true
Movies:ServiceApiKey = 12345
在上述範例中,索引鍵名稱中的冒號表示 中的 secrets.json
物件階層。
移除單一秘密
假設應用程式的 secrets.json
檔案包含下列兩個秘密:
{
"Movies:ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true",
"Movies:ServiceApiKey": "12345"
}
從專案檔所在的目錄中執行下列命令:
dotnet user-secrets remove "Movies:ConnectionString"
已修改應用程式的 secrets.json
檔案,以移除與 Movies:ConnectionString
索引鍵相關聯的機碼/值組:
{
"Movies": {
"ServiceApiKey": "12345"
}
}
dotnet user-secrets list
會顯示下列訊息:
Movies:ServiceApiKey = 12345
移除所有秘密
假設應用程式的 secrets.json
檔案包含下列兩個秘密:
{
"Movies:ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true",
"Movies:ServiceApiKey": "12345"
}
從專案檔所在的目錄中執行下列命令:
dotnet user-secrets clear
應用程式的所有使用者密碼都已從 secrets.json
檔案中刪除:
{}
執行 dotnet user-secrets list
會顯示下列訊息:
No secrets configured for this application.
使用 Visual Studio 管理使用者密碼
若要在 Visual Studio 中管理使用者密碼,請以滑鼠右鍵按一下方案總管中的專案,然後選取 [ 管理使用者密碼 ]:
將使用者密碼從 ASP.NET Framework 移轉至 ASP.NET Core
請參閱這個 GitHub 問題。
其他資源
- 如需從 IIS 存取使用者秘密的資訊,請參閱 此問題 和 此問題 。
- ASP.NET Core 中的組態
- ASP.NET Core 中的 Azure 金鑰保存庫 組態提供者
由 裡克·安德森 、 柯克·拉金 、 丹尼爾·羅斯 和 斯科特·艾迪
檢視或下載範例程式碼 \(英文\) (如何下載)
本檔說明如何在開發電腦上管理 ASP.NET Core 應用程式的敏感性資料。 永遠不要將密碼或其他敏感性資料儲存在原始程式碼中。 生產秘密不應該用於開發或測試。 不應使用應用程式部署秘密。 相反地,應該透過環境變數或 Azure 金鑰保存庫等受控制的方法來存取生產秘密。 您可以透過 Azure Key Vault 設定提供者儲存及保護 Azure 測試與生產祕密。
環境變數
環境變數可用來避免在程式碼或本機組態檔中儲存應用程式秘密。 環境變數會覆寫所有先前指定之組態來源的組態值。
請考慮啟用個別使用者帳戶 安全性的 ASP.NET Core Web 應用程式 。 預設資料庫連接字串包含在專案 appsettings.json
檔案中,且索引鍵 DefaultConnection
為 。 預設連接字串適用于 LocalDB,其會在使用者模式中執行,而且不需要密碼。 在應用程式部署期間, DefaultConnection
可以使用環境變數的值覆寫索引鍵值。 環境變數可能會使用敏感性認證來儲存完整的連接字串。
警告
環境變數通常會以純文字、未加密的文字儲存。 如果電腦或進程遭到入侵,則不受信任的合作物件可以存取環境變數。 可能需要額外的措施,以防止洩漏使用者秘密。
:
分隔符號不適用於所有平台上的環境變數階層式機碼。 __
,雙底線,為:
- 所有平台都支援。 例如,Bash 不支援
:
分隔符號,但支援__
。 - 自動由
:
取代
秘密管理員
秘密管理員工具會在開發 ASP.NET Core 專案時儲存敏感性資料。 在此內容中,機密資料片段是應用程式秘密。 應用程式秘密會儲存在專案樹狀目錄的個別位置。 應用程式秘密會與特定專案相關聯,或跨數個專案共用。 應用程式秘密不會簽入原始檔控制。
警告
秘密管理員工具不會加密儲存的秘密,不應被視為受信任的存放區。 僅供開發之用。 索引鍵和值會儲存在 JS 使用者設定檔目錄中的 ON 組態檔中。
秘密管理員工具的運作方式
秘密管理員工具會隱藏實作詳細資料,例如儲存值的位置和方式。 您可以在不知道這些實作詳細資料的情況下使用此工具。 這些值會儲存在 JS 本機電腦使用者設定檔資料夾中的 ON 檔案中:
檔案系統路徑:
%APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secrets.json
在上述檔案路徑中,將 取代 <user_secrets_id>
為 UserSecretsId
專案檔中指定的值。
請勿撰寫程式碼,此程式碼取決於使用秘密管理員工具儲存的資料位置或格式。 這些實作詳細資料可能會變更。 例如,秘密值不會加密,但可能在未來。
啟用秘密儲存體
Secret Manager 工具會針對儲存在使用者設定檔中的專案特定組態設定運作。
秘密管理員工具組含 .NET Core SDK 3.0.100 或更新版本中的 init
命令。 若要使用使用者密碼,請在專案目錄中執行下列命令:
dotnet user-secrets init
上述命令會在專案檔的 內 PropertyGroup
加入 UserSecretsId
專案。 根據預設,的內部文字 UserSecretsId
是 GUID。 內部文字是任意的,但對專案而言是唯一的。
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UserSecretsId>79a3edd0-2092-40a2-a04d-dcb46d5ca9ed</UserSecretsId>
</PropertyGroup>
在 Visual Studio 中,以滑鼠右鍵按一下 方案總管 中的專案,然後從操作功能表中選取 [ 管理使用者密碼 ]。 此筆勢會將填入 GUID 的專案新增 UserSecretsId
至專案檔。
設定秘密
定義由金鑰及其值組成的應用程式秘密。 秘密與專案 UserSecretsId
的值相關聯。 例如,從專案檔所在的目錄執行下列命令:
dotnet user-secrets set "Movies:ServiceApiKey" "12345"
在上述範例中,冒號表示 Movies
是具有 ServiceApiKey
屬性的物件常值。
秘密管理員工具也可以從其他目錄使用。 --project
使用 選項來提供專案檔所在的檔案系統路徑。 例如:
dotnet user-secrets set "Movies:ServiceApiKey" "12345" --project "C:\apps\WebApp1\src\WebApp1"
JSVisual Studio 中的 ON 結構扁平化
Visual Studio 的 [管理使用者密碼] 手勢會在文字編輯器中開啟檔案 secrets.json
。 將 的內容 secrets.json
取代為要儲存的索引鍵/值組。 例如:
{
"Movies": {
"ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true",
"ServiceApiKey": "12345"
}
}
透過 或 dotnet user-secrets set
修改 JSdotnet user-secrets remove
之後,ON 結構會扁平化。 例如,執行 dotnet user-secrets remove "Movies:ConnectionString"
會 Movies
折迭物件常值。 修改過的檔案類似下列 JS ON:
{
"Movies:ServiceApiKey": "12345"
}
設定多個秘密
您可以透過將 ON 傳送 JS 至 set
命令,來設定一批秘密。 在下列範例中,檔案 input.json
的內容會透過管道傳送至 set
命令。
開啟命令殼層,然後執行下列命令:
type .\input.json | dotnet user-secrets set
存取秘密
若要存取秘密,請完成下列步驟:
註冊使用者秘密組態來源
使用者秘密設定 提供者 會向 .NET 組態 API 註冊適當的組態來源。
專案呼叫 CreateDefaultBuilder 時,會自動在開發模式中新增使用者秘密組態來源。 CreateDefaultBuilder
當 為 Development 時 EnvironmentName 呼叫 AddUserSecrets :
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
未呼叫 時 CreateDefaultBuilder
,請在 中 ConfigureAppConfiguration 呼叫 AddUserSecrets ,明確新增使用者秘密組態來源。 AddUserSecrets
只有在開發環境中執行應用程式時呼叫 ,如下列範例所示:
public class Program
{
public static void Main(string[] args)
{
var host = new HostBuilder()
.ConfigureAppConfiguration((hostContext, builder) =>
{
// Add other providers for JSON, etc.
if (hostContext.HostingEnvironment.IsDevelopment())
{
builder.AddUserSecrets<Program>();
}
})
.Build();
host.Run();
}
}
透過設定 API 讀取秘密
如果註冊使用者密碼組態來源,.NET 組態 API 可以讀取秘密。 建構函式插入 可用來取得 .NET 組態 API 的存取權。 請考慮閱讀金鑰的 Movies:ServiceApiKey
下列範例:
啟動類別:
public class Startup
{
private string _moviesApiKey = null;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
_moviesApiKey = Configuration["Movies:ServiceApiKey"];
}
public void Configure(IApplicationBuilder app)
{
app.Run(async (context) =>
{
var result = string.IsNullOrEmpty(_moviesApiKey) ? "Null" : "Not Null";
await context.Response.WriteAsync($"Secret is {result}");
});
}
}
Razor 頁面頁面模型:
public class IndexModel : PageModel
{
private readonly IConfiguration _config;
public IndexModel(IConfiguration config)
{
_config = config;
}
public void OnGet()
{
var moviesApiKey = _config["Movies:ServiceApiKey"];
// call Movies service with the API key
}
}
如需詳細資訊,請參閱 頁面 的啟動 和 存取設定中的 Razor 存取組態。
將秘密對應至 POCO
將整個物件常值對應至 POCO(具有屬性的簡單 .NET 類別)對於匯總相關屬性很有用。
假設應用程式的 secrets.json
檔案包含下列兩個秘密:
{
"Movies:ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true",
"Movies:ServiceApiKey": "12345"
}
若要將上述秘密對應至 POCO,請使用 .NET 組態 API 的物件 圖形系結 功能。 下列程式碼系結至自訂 MovieSettings
POCO 並存取 ServiceApiKey
屬性值:
var moviesConfig =
Configuration.GetSection("Movies").Get<MovieSettings>();
_moviesApiKey = moviesConfig.ServiceApiKey;
Movies:ConnectionString
和 Movies:ServiceApiKey
秘密會對應至 中的 MovieSettings
個別屬性:
public class MovieSettings
{
public string ConnectionString { get; set; }
public string ServiceApiKey { get; set; }
}
使用秘密取代字串
將密碼儲存在純文字不安全。 例如,儲存在 中的 appsettings.json
資料庫連接字串可能包含指定使用者的密碼:
{
"ConnectionStrings": {
"Movies": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;User Id=johndoe;Password=pass123;MultipleActiveResultSets=true"
}
}
更安全的方法是將密碼儲存為秘密。 例如:
dotnet user-secrets set "DbPassword" "pass123"
Password
從 中的 appsettings.json
連接字串中移除機碼/值組。 例如:
{
"ConnectionStrings": {
"Movies": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;User Id=johndoe;MultipleActiveResultSets=true"
}
}
秘密的值可以在物件的 Password 屬性上 SqlConnectionStringBuilder 設定,以完成連接字串:
public class Startup
{
private string _connection = null;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
var builder = new SqlConnectionStringBuilder(
Configuration.GetConnectionString("Movies"));
builder.Password = Configuration["DbPassword"];
_connection = builder.ConnectionString;
// code omitted for brevity
}
public void Configure(IApplicationBuilder app)
{
app.Run(async (context) =>
{
await context.Response.WriteAsync($"DB Connection: {_connection}");
});
}
}
列出秘密
假設應用程式的 secrets.json
檔案包含下列兩個秘密:
{
"Movies:ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true",
"Movies:ServiceApiKey": "12345"
}
從專案檔所在的目錄中執行下列命令:
dotnet user-secrets list
會出現下列輸出:
Movies:ConnectionString = Server=(localdb)\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true
Movies:ServiceApiKey = 12345
在上述範例中,索引鍵名稱中的冒號表示 中的 secrets.json
物件階層。
移除單一秘密
假設應用程式的 secrets.json
檔案包含下列兩個秘密:
{
"Movies:ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true",
"Movies:ServiceApiKey": "12345"
}
從專案檔所在的目錄中執行下列命令:
dotnet user-secrets remove "Movies:ConnectionString"
已修改應用程式的 secrets.json
檔案,以移除與 MoviesConnectionString
索引鍵相關聯的機碼/值組:
{
"Movies": {
"ServiceApiKey": "12345"
}
}
dotnet user-secrets list
會顯示下列訊息:
Movies:ServiceApiKey = 12345
移除所有秘密
假設應用程式的 secrets.json
檔案包含下列兩個秘密:
{
"Movies:ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true",
"Movies:ServiceApiKey": "12345"
}
從專案檔所在的目錄中執行下列命令:
dotnet user-secrets clear
應用程式的所有使用者密碼都已從 secrets.json
檔案中刪除:
{}
執行 dotnet user-secrets list
會顯示下列訊息:
No secrets configured for this application.
使用 Visual Studio 管理使用者密碼
若要在 Visual Studio 中管理使用者密碼,請以滑鼠右鍵按一下方案總管中的專案,然後選取 [ 管理使用者密碼 ]:
將使用者密碼從 ASP.NET Framework 移轉至 ASP.NET Core
請參閱這個 GitHub 問題。
其他資源
- 如需從 IIS 存取使用者秘密的資訊,請參閱 此問題 和 此問題 。
- ASP.NET Core 中的組態
- ASP.NET Core 中的 Azure 金鑰保存庫 組態提供者