Safe storage of application secrets in PSS®X development¶
Overview¶
Working with secure keys in ASP.NET Core has always been a delicate balance of convenience and safety. It’s easy to add a production database connection string to your application’s configuration for quick testing, only to accidentally check it into source control.
Danger
Never store passwords or other sensitive data in source code.
Production secrets shouldn't be used for development or test. Secrets shouldn't be deployed with the app. Instead, production secrets should be accessed through a controlled means like environment variables, secret providers etc.
In an ASP.NET Core application, configuration settings are managed using key-value pairs provided by configuration providers. Configuration providers read configuration data into key-value pairs from a variety of configuration source like settings files, environment variables, etc. The appsettings.json
file, which is the default configuration file, contains various settings for your application.
This is how a configuration file is added to an application:
...
builder.Host
.ConfigureAppConfiguration((context, config) =>
{
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); # File based
config.AddEnvironmentVariables(); # Environment value based
};
...
and this is how a configuration may look like for a connection string looks like:
"ConnectionStrings": {
"Default": "pssx-sql-server;User Id=sa;Password=myPassword;Initial Catalog=pssx-app;MultipleActiveResultSets=true;TrustServerCertificate=True",
"Hangfire": "pssx-sql-server;User Id=sa;Password=myPassword;Initial Catalog=pssx-hangfire;MultipleActiveResultSets=true;TrustServerCertificate=True"
}
In the above example the password is stored in plain text and unencrypted and this is not safe.
Secret Manager¶
.NET SDK has a secrets manager, a command-line utility that uses your current project to create a unique store for project-related secrets. When initialized, .NET will store your project secrets in a user-specific directory outside the current project. Depending on your host operating system, these folders differ on Windows and Linux/macOS.
File system path:
%APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secrets.json
File system path:
~/.microsoft/usersecrets/<user_secrets_id>/secrets.json
In the preceding file paths, replace <user_secrets_id>
with the UserSecretsId
value specified in the project file.
Don't write code that depends on the location or format of data saved with the Secret Manager tool. These implementation details may change.
Enable secret storage¶
The Secret Manager tool operates on project-specific configuration settings stored in your user profile.
- Do use MSBuild properties as your
UserSecretsId
value.
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<UserSecretsId>$(MSBuildProjectName)</UserSecretsId>
</PropertyGroup>
</Project>
Use the CLI¶
You can also run the following command from terminal in the project directory.
dotnet user-secrets init --project src/GridLab.<ApplicationName>.AuthServer --id GridLab.<ApplicationName>.AuthServer
dotnet user-secrets init --project src/GridLab.<ApplicationName>.HttpApi.Host --id GridLab.<ApplicationName>.HttpApi.Host
dotnet user-secrets init --project src/GridLab.<ApplicationName>.Web --id GridLab.<ApplicationName>.Web
dotnet user-secrets init --project src/GridLab.<ApplicationName>.Web.Public --id GridLab.<ApplicationName>.Web.Public
Use Visual Studio¶
In Visual Studio, right-click the project in Solution Explorer, and select Manage User Secrets from the context menu.
This gesture adds a UserSecretsId
element, populated with a GUID, to the project file if you didn't set property before.
Register the user secrets configuration source¶
The user secrets configuration provider registers the appropriate configuration source with the .NET Configuration API.
var builder = WebApplication.CreateBuilder(args);
builder.Host
.AddAppSettingsSecretsJson()
.UseAutofac()
.UseSerilog();
await builder.AddApplicationAsync<PSSXAuthServerModule>();
var app = builder.Build();
WebApplication.CreateBuilder
initializes a new instance of the WebApplicationBuilder
class with preconfigured defaults. The initialized WebApplicationBuilder
(builder) provides default configuration and calls AddUserSecrets when the EnvironmentName
is Development
.
Set a secret¶
Define an app secret consisting of a key and its value. The secret is associated with the project's UserSecretsId
value.
For example, run the following command from the directory in which the project file exists:
dotnet user-secrets set "Swagger:ClientSecret" "1q2w3e*" --project src/GridLab.<ApplicationName>.HttpApi.Host
JSON structure flattening in Visual Studio¶
Visual Studio's Manage User Secrets gesture opens a secrets.json
file in the text editor. Replace the contents of secrets.json
with the key-value pairs to be stored. For example:
{
"Swagger": {
"ClientId": "PSSX_Swagger",
"ClientSecret": "1q2w3e*"
}
}
The JSON structure is flattened after modifications via dotnet user-secrets remove
or dotnet user-secrets set
. For example, running dotnet user-secrets remove "Swagger:ClientId"
collapses the Swagger object literal.
The modified file resembles the following JSON:
{
"Swagger:ClientSecret": "1q2w3e*"
}
Set multiple secrets¶
A batch of secrets can be set by piping JSON to the set
command. In the following example, the input.json
file's contents are piped to the set
command.
Open a command shell, and execute the following command:
type .\input.json | dotnet user-secrets set
Open a command shell, and execute the following command:
cat ./input.json | dotnet user-secrets set