保管庫 ASP.NET Core開發中應用程式秘密的儲存體

作者 :Rick AndersonKirk Larkin

檢視或下載範例程式碼 (如何下載)

本檔說明如何在開發電腦上管理 ASP.NET Core應用程式的敏感性資料。 請勿將密碼或其他敏感性資料儲存在原始程式碼中。 生產秘密不應該用於開發或測試。 秘密不應與應用程式一起部署。 相反地,應該透過受控方式存取生產秘密,例如環境變數或 Azure 金鑰保存庫。 您可以透過 Azure Key Vault 設定提供者儲存及保護 Azure 測試與生產祕密。

環境變數

環境變數可用來避免在程式碼或本機組態檔中儲存應用程式密碼。 環境變數會覆寫所有先前指定之組態來源的組態值。

請考慮啟用個別使用者帳戶安全性的 ASP.NET Core Web 應用程式。 預設資料庫連接字串會包含在專案的 appsettings.json 檔案中,其中包含索引鍵 DefaultConnection 。 預設連接字串適用于在使用者模式中執行的 LocalDB,而且不需要密碼。 在應用程式部署期間, DefaultConnection 可以使用環境變數的值覆寫索引鍵值。 環境變數可能會使用敏感性認證來儲存完整的連接字串。

警告

環境變數通常會以純文字、未加密的文字儲存。 如果電腦或進程遭到入侵,則不受信任的合作物件可以存取環境變數。 可能需要額外的措施,以防止洩漏使用者密碼。

分隔 : 符不適用於所有平臺上的環境變數階層式索引鍵。 __,雙底線為:

  • 所有平臺都支援。 例如, :Bash不支援分隔符號,但 __ 是 。
  • 自動取代為 :

秘密管理員

秘密管理員工具會在開發 ASP.NET Core專案期間儲存敏感性資料。 在此內容中,敏感性資料片段是應用程式秘密。 應用程式秘密會儲存在與專案樹狀目錄不同的位置。 應用程式秘密會與特定專案相關聯,或跨數個專案共用。 應用程式秘密不會簽入原始檔控制。

警告

秘密管理員工具不會加密儲存的秘密,不應被視為信任的存放區。 僅供開發之用。 索引鍵和值會儲存在使用者設定檔目錄中的 JSON 組態檔中。

秘密管理員工具的運作方式

秘密管理員工具會隱藏實作詳細資料,例如儲存值的位置和方式。 您可以使用此工具,而不需要知道這些實作詳細資料。 這些值會儲存在本機電腦使用者設定檔資料夾中的 JSON 檔案中:

檔案系統路徑:

%APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secrets.json

在上述檔案路徑中,將 取代 <user_secrets_id>UserSecretsId 專案檔中指定的值。

請勿撰寫取決於使用秘密管理員工具儲存之資料的位置或格式的程式碼。 這些實作詳細資料可能會變更。 例如,秘密值不會加密,但可能在未來。

啟用秘密儲存體

秘密管理員工具會在儲存在使用者設定檔中的專案特定組態設定上運作。

秘密管理員工具組含 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"

VISUAL STUDIO中的 JSON 結構壓平合併

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 修改 dotnet user-secrets remove 之後,JSON 結構會扁平化。 例如,執行 dotnet user-secrets remove "Movies:ConnectionString"Movies 折迭物件常值。 修改過的檔案類似下列 JSON:

{
  "Movies:ServiceApiKey": "12345"
}

設定多個秘密

您可以將 JSON 管線設定為命令, set 以設定一批秘密。 在下列範例中,檔案 input.json 的內容會以管線傳送至 set 命令。

開啟命令殼層,然後執行下列命令:

type .\input.json | dotnet user-secrets set

存取秘密

若要存取秘密,請完成下列步驟:

  1. 註冊使用者秘密組態來源
  2. 透過組態 API 讀取秘密

註冊使用者秘密組態來源

使用者秘密組 態提供者 會向 .NET 組態 API註冊適當的組態來源。

ASP.NET Core使用dotnet new或 Visual Studio建立的 Web 應用程式產生下列程式碼:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

WebApplication.CreateBuilder 會使用預先設定的 WebApplicationBuilder 預設值,初始化 類別的新實例。 初始化 WebApplicationBuilder 的 (builder) 會在 為 DevelopmentEnvironmentName 提供預設組態和呼叫 AddUserSecrets

透過組態 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:ConnectionStringMovies: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 檔案已修改為移除與機碼相關聯的 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中的使用者密碼,請以滑鼠右鍵按一下方案總管中的專案,然後選取 [管理使用者密碼]:

Visual Studio showing Manage User Secrets

其他資源

作者: Rick AndersonKirk LarkinDaniel RothScott Addie

檢視或下載範例程式碼 (如何下載)

本檔說明如何在開發電腦上管理 ASP.NET Core應用程式的敏感性資料。 請勿將密碼或其他敏感性資料儲存在原始程式碼中。 生產秘密不應該用於開發或測試。 秘密不應與應用程式一起部署。 相反地,應透過受控方式存取生產秘密,例如環境變數或 Azure 金鑰保存庫。 您可以透過 Azure Key Vault 設定提供者儲存及保護 Azure 測試與生產祕密。

環境變數

環境變數可用來避免在程式碼或本機組態檔中儲存應用程式秘密。 環境變數會覆寫所有先前指定之組態來源的組態值。

請考慮啟用個別使用者帳戶安全性的 ASP.NET Core Web 應用程式。 預設資料庫連接字串會包含在專案 appsettings.json 檔案中,且索引鍵 DefaultConnection 為 。 預設連接字串適用于LocalDB,其會在使用者模式中執行,而且不需要密碼。 在應用程式部署期間, DefaultConnection 可以使用環境變數的值覆寫索引鍵值。 環境變數可能會使用敏感性認證來儲存完整的連接字串。

警告

環境變數通常會以純文字、未加密的文字儲存。 如果電腦或進程遭到入侵,則不受信任的合作物件可以存取環境變數。 可能需要其他措施來防止洩漏使用者密碼。

分隔 : 符不適用於所有平臺上的環境變數階層式索引鍵。 __,雙底線為:

  • 所有平臺都支援。 例如, :Bash不支援分隔符號,但 __ 是 。
  • 自動取代為 :

秘密管理員

秘密管理員工具會在開發 ASP.NET Core專案期間儲存敏感性資料。 在此內容中,敏感性資料是應用程式秘密。 應用程式秘密會儲存在與專案樹狀結構不同的位置。 應用程式秘密會與特定專案相關聯,或跨數個專案共用。 應用程式秘密不會簽入原始檔控制。

警告

秘密管理員工具不會加密儲存的秘密,不應視為受信任的存放區。 僅供開發之用。 索引鍵和值會儲存在使用者設定檔目錄中的 JSON 組態檔中。

秘密管理員工具的運作方式

秘密管理員工具會隱藏實作詳細資料,例如儲存值的位置和方式。 您可以使用此工具,而不知道這些實作詳細資料。 這些值會儲存在本機電腦使用者設定檔資料夾中的 JSON 檔案中:

檔案系統路徑:

%APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secrets.json

在上述檔案路徑中,將 取代 <user_secrets_id>UserSecretsId 專案檔中指定的值。

請勿撰寫取決於使用秘密管理員工具儲存之資料的位置或格式的程式碼。 這些實作詳細資料可能會變更。 例如,秘密值不會加密,但可能在未來。

啟用秘密儲存體

秘密管理員工具會在儲存在使用者設定檔中的專案特定組態設定上運作。

秘密管理員工具組含 .NET Core SDK 3.0.100 或更新版本中的 init 命令。 若要使用使用者密碼,請在專案目錄中執行下列命令:

dotnet user-secrets init

上述命令會在 UserSecretsId 專案檔的 內 PropertyGroup 加入 專案。 根據預設,的內部 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"

VISUAL STUDIO中的 JSON 結構扁平化

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 修改之後 dotnet user-secrets remove ,JSON 結構會扁平化。 例如,執行 dotnet user-secrets remove "Movies:ConnectionString"Movies 折迭物件常值。 修改過的檔案類似下列 JSON:

{
  "Movies:ServiceApiKey": "12345"
}

設定多個秘密

您可以將 JSON 傳送至 set 命令來設定秘密批次。 在下列範例中,檔案 input.json 的內容會以管線傳送至 set 命令。

開啟命令殼層,然後執行下列命令:

type .\input.json | dotnet user-secrets set

存取秘密

若要存取秘密,請完成下列步驟:

  1. 註冊使用者秘密設定來源
  2. 透過設定 API 讀取秘密

註冊使用者秘密設定來源

使用者秘密設定 提供者 會向 .NET 組態 API註冊適當的組態來源。

專案呼叫 CreateDefaultBuilder 時,會自動在開發模式中新增使用者秘密組態來源。 CreateDefaultBuilder當 為 Development 時呼叫 : AddUserSecretsEnvironmentName

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:ConnectionStringMovies: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中的使用者密碼,請以滑鼠右鍵按一下方案總管中的專案,然後選取 [管理使用者密碼]:

Visual Studio showing Manage User Secrets

其他資源