在 ASP.NET Core 开发环境中管理应用机密

作者:Rick Anderson 和 Kirk Larkin

本文介绍如何在开发计算机上管理 ASP.NET Core 应用的敏感数据。 切勿将密码或其他敏感数据存储在源代码或配置文件中。 不得将生产机密用于开发或测试。 机密不得随应用一起部署。 应通过受控方式(如 Azure 密钥库)访问生产机密。 Azure 测试和生产机密可以使用 Azure 密钥库配置提供程序进行存储和保护。

查看或下载示例代码 (如何下载)

有关已部署测试和生产应用的身份验证的详细信息,请参阅安全身份验证流。

若要在.NET控制台应用中使用用户机密,请参阅 GitHub dotnet/entityframework.docs 问题 #3939。

使用环境变量

环境变量用于避免在代码或本地配置文件中存储应用机密。 环境变量会替代之前指定的所有配置源的配置值。

请考虑启用 个人帐户 安全性的 ASP.NET 核心 Web 应用。 项目的 appsettings.json 文件中包含一个默认数据库连接字符串,使用 DefaultConnection 键。 默认连接字符串适用于 LocalDB,它在用户模式下运行,不需要密码。 在应用部署期间,可以使用环境变量中的值替代 DefaultConnection 密钥值。 环境变量可能会存储包含敏感凭据的完整连接字符串。

警告

环境变量通常存储为纯文本、未加密的文本。 如果计算机或进程遭到入侵,则不受信任的参与方可以访问环境变量。 可能需要采取额外的措施来防止泄露用户机密。

所有平台上的环境变量分层键都不支持冒号 (:) 分隔符。 例如, Bash 不支持将冒号(:)作为分隔符。 所有平台都支持双下划线(__)语法,并自动将其替换为冒号(:)。

使用机密管理器工具

机密管理器是一种在应用程序开发过程中存储敏感数据的工具。 在此上下文中,一段敏感数据是 应用机密。

  • 应用机密存储在与项目树不同的位置。
  • 它们与特定项目相关联,或跨多个项目共享。
  • 它们未签入版本控制系统。

警告

机密管理器不会加密存储的机密,不应被视为受信任的存储。 它仅用于开发用途。 密钥和值存储在用户配置文件目录中的 JSON 配置文件中。

机密管理器隐藏实现详细信息,例如值存储位置和方式。 可在不知道这些实现详细信息的情况下使用该工具。 这些值存储在本地计算机的用户配置文件文件夹中的 JSON 文件中:

文件系统路径:

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

在文件系统路径中,将 <user_secrets_id> 部分替换为项目文件中指定的 UserSecretsId 值。

不要编写依赖于使用机密管理器保存的数据的位置或格式的代码。 这些实现详细信息可能会更改。 例如,机密值不会加密。

启用机密存储

机密管理器针对存储在用户配置文件中的特定于项目的配置设置进行操作。

使用 CLI

机密管理器包括一个 init 命令。 若要使用用户机密,请在项目目录中运行以下命令:

dotnet user-secrets init

此命令在项目文件的 PropertyGroup 中添加一个 UserSecretsId 元素。 默认情况下,UserSecretsId 的内部文本是 GUID。 内部文本是任意的,但对于项目来说是唯一的。 以下示例显示 GUID 值 `0000a1a1-b2b2-c3c3-d4d4-eeeeee555555`。

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <UserSecretsId>0000a1a1-b2b2-c3c3-d4d4-eeeeee555555</UserSecretsId>
  </PropertyGroup>

</Project>

使用 Visual Studio

在 Visual Studio 中,在解决方案资源管理器中右键单击该项目,然后从上下文菜单中选择“管理用户机密”。 该手势会将 UserSecretsId 元素(填充有 GUID)添加到项目文件中。

如果“GenerateAssemblyInfo”为“false”

如果禁用了程序集信息属性的生成(GenerateAssemblyInfo)(设置为 false),请手动在 AssemblyInfo.cs 文件中添加 UserSecretsIdAttribute。 例如:

[assembly: UserSecretsId("your_user_secrets_id")]

将属性手动添加到UserSecretsIdAssemblyInfo.cs文件时,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 Manage User Secrets 手势在文本编辑器中打开一个 secrets.json 文件。 将 secrets.json 文件的内容替换为要存储的键值对。 例如:

{
  "Movies": {
    "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true",
    "ServiceApiKey": "12345"
  }
}

通过 dotnet user-secrets remove 或 dotnet user-secrets set 命令修改后,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 命令创建的 Web 应用或Visual Studio生成以下代码:

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

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

app.Run();

WebApplication.CreateBuilder 方法使用预先配置的默认设置初始化 WebApplicationBuilder 类的新实例。 已初始化的WebApplicationBuilder(builder)提供默认配置,并在EnvironmentName属性为Development时调用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:ConnectionString 和 Movies:ServiceApiKey 机密映射到 MovieSettings 中的相应属性:

public class MovieSettings
{
    public string ConnectionString { get; set; }

    public string ServiceApiKey { get; set; }
}

结合机密信息使用字符串替换

以纯文本形式存储密码不太安全。 切勿将机密存储在配置文件(例如 appsettings.json)中,该文件可能会签入源代码存储库。

例如,存储在 appsettings.json 文件中的数据库连接字符串不应包含密码。 而是将密码存储为机密,并在运行时将密码包含在连接字符串中。 例如:

dotnet user-secrets set "DbPassword" "`<secret value>`"

将 <secret value> 示例中的占位符替换为密码值。 在对象的SqlConnectionStringBuilder属性上Password设置机密的值,将其作为密码值包含在连接字符串中:

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 中管理用户机密,请右键单击解决方案资源管理器中的项目,然后选择 Manage User Secrets:

屏幕截图显示如何在 Visual Studio 中选择“管理用户机密”选项。

将用户机密从 ASP.NET Framework 迁移到 ASP.NET Core

可以将存储的用户机密从 ASP.NET Framework 迁移到 ASP.NET Core。 有关更多信息,请参阅 GitHub dotnet/aspnetcore.docs 议题 #27611 – User Secrets 文档未提及其与 AssemblyInfo.cs 不兼容。

在非 Web 应用程序中使用用户机密

面向 Microsoft.NET.Sdk.Web 的项目会自动包括对用户机密的支持。 对于面向 Microsoft.NET.Sdk 的项目,例如控制台应用程序,请明确安装配置扩展和用户机密的 NuGet 包。

Install-Package Microsoft.Extensions.Configuration
Install-Package Microsoft.Extensions.Configuration.UserSecrets

安装包后,以与 Web 应用相同的方式 初始化项目 并 设置机密 。 以下示例演示了一个控制台应用程序,它检索使用 AppSecret 键设置的机密值:

using Microsoft.Extensions.Configuration;

namespace ConsoleApp;

class Program
{
    static void Main(string[] args)
    {
        IConfigurationRoot config = new ConfigurationBuilder()
            .AddUserSecrets<Program>()
            .Build();

        Console.WriteLine(config["AppSecret"]);
    }
}

原文:在 ASP.NET Core 开发环境中管理应用机密。作者/来源:Microsoft Learn 文档贡献者。本文依据所列原文整理为中文,代码、命令与配置示例保留原文。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容