|
|
|
@ -1,10 +1,11 @@
|
|
|
|
|
---
|
|
|
|
|
title: Safe storage of app secrets in development in ASP.NET Core
|
|
|
|
|
author: rick-anderson
|
|
|
|
|
description: Shows how to safely store secrets during development
|
|
|
|
|
description: Learn how to store and retrieve sensitive information as app secrets during the development of an ASP.NET Core app.
|
|
|
|
|
manager: wpickett
|
|
|
|
|
ms.author: riande
|
|
|
|
|
ms.date: 09/15/2017
|
|
|
|
|
ms.author: scaddie
|
|
|
|
|
ms.custom: mvc
|
|
|
|
|
ms.date: 05/16/2018
|
|
|
|
|
ms.prod: asp.net-core
|
|
|
|
|
ms.technology: aspnet
|
|
|
|
|
ms.topic: article
|
|
|
|
@ -12,114 +13,246 @@ uid: security/app-secrets
|
|
|
|
|
---
|
|
|
|
|
# Safe storage of app secrets in development in ASP.NET Core
|
|
|
|
|
|
|
|
|
|
By [Rick Anderson](https://twitter.com/RickAndMSFT), [Daniel Roth](https://github.com/danroth27), and [Scott Addie](https://scottaddie.com)
|
|
|
|
|
By [Rick Anderson](https://twitter.com/RickAndMSFT), [Daniel Roth](https://github.com/danroth27), and [Scott Addie](https://github.com/scottaddie)
|
|
|
|
|
|
|
|
|
|
This document shows how you can use the Secret Manager tool in development to keep secrets out of your code. The most important point is you should never store passwords or other sensitive data in source code, and you shouldn't use production secrets in development and test mode. You can instead use the [configuration](xref:fundamentals/configuration/index) system to read these values from environment variables or from values stored using the Secret Manager tool. The Secret Manager tool helps prevent sensitive data from being checked into source control. The [configuration](xref:fundamentals/configuration/index) system can read secrets stored with the Secret Manager tool described in this article.
|
|
|
|
|
::: moniker range="<= aspnetcore-1.1"
|
|
|
|
|
[View or download sample code](https://github.com/aspnet/Docs/tree/master/aspnetcore/security/app-secrets/samples/1.1) ([how to download](xref:tutorials/index#how-to-download-a-sample))
|
|
|
|
|
::: moniker-end
|
|
|
|
|
::: moniker range=">= aspnetcore-2.0"
|
|
|
|
|
[View or download sample code](https://github.com/aspnet/Docs/tree/master/aspnetcore/security/app-secrets/samples/2.1) ([how to download](xref:tutorials/index#how-to-download-a-sample))
|
|
|
|
|
::: moniker-end
|
|
|
|
|
|
|
|
|
|
The Secret Manager tool is used only in development. You can safeguard Azure test and production secrets with the [Microsoft Azure Key Vault](https://azure.microsoft.com/services/key-vault/) configuration provider. See [Azure Key Vault configuration provider](xref:security/key-vault-configuration) for more information.
|
|
|
|
|
This document explains techniques for storing and retrieving sensitive data during the development of an ASP.NET Core app. You should never store passwords or other sensitive data in source code, and you shouldn't use production secrets in development or test mode. You can store and protect Azure test and production secrets with the [Azure Key Vault configuration provider](xref:security/key-vault-configuration).
|
|
|
|
|
|
|
|
|
|
## Environment variables
|
|
|
|
|
|
|
|
|
|
To avoid storing app secrets in code or in local configuration files, you store secrets in environment variables. You can setup the [configuration](xref:fundamentals/configuration/index) framework to read values from environment variables by calling `AddEnvironmentVariables`. You can then use environment variables to override configuration values for all previously specified configuration sources.
|
|
|
|
|
Environment variables are used to avoid storage of app secrets in code or in local configuration files. Environment variables override configuration values for all previously specified configuration sources.
|
|
|
|
|
|
|
|
|
|
For example, if you create a new ASP.NET Core web app with individual user accounts, it will add a default connection string to the *appsettings.json* file in the project with the key `DefaultConnection`. The default connection string is setup to use LocalDB, which runs in user mode and doesn't require a password. When you deploy your application to a test or production server, you can override the `DefaultConnection` key value with an environment variable setting that contains the connection string (potentially with sensitive credentials) for a test or production database server.
|
|
|
|
|
::: moniker range="<= aspnetcore-1.1"
|
|
|
|
|
Configure the reading of environment variable values by calling [AddEnvironmentVariables](/dotnet/api/microsoft.extensions.configuration.environmentvariablesextensions.addenvironmentvariables) in the `Startup` constructor:
|
|
|
|
|
|
|
|
|
|
>[!WARNING]
|
|
|
|
|
> Environment variables are generally stored in plain text and are not encrypted. If the machine or process is compromised, then environment variables can be accessed by untrusted parties. Additional measures to prevent disclosure of user secrets may still be required.
|
|
|
|
|
[!code-csharp[](app-secrets/samples/1.1/UserSecrets/Startup.cs?name=snippet_StartupConstructor&highlight=10)]
|
|
|
|
|
::: moniker-end
|
|
|
|
|
|
|
|
|
|
Consider an ASP.NET Core web app in which **Individual User Accounts** security is enabled. A default database connection string is included in the project's *appsettings.json* file with the key `DefaultConnection`. The default connection string is for LocalDB, which runs in user mode and doesn't require a password. During app deployment, the `DefaultConnection` key value can be overridden with an environment variable's value. The environment variable may store the complete connection string with sensitive credentials.
|
|
|
|
|
|
|
|
|
|
> [!WARNING]
|
|
|
|
|
> Environment variables are generally stored in plain, unencrypted text. If the machine or process is compromised, environment variables can be accessed by untrusted parties. Additional measures to prevent disclosure of user secrets may be required.
|
|
|
|
|
|
|
|
|
|
## Secret Manager
|
|
|
|
|
|
|
|
|
|
The Secret Manager tool stores sensitive data for development work outside of your project tree. The Secret Manager tool is a project tool that can be used to store secrets for a .NET Core project during development. With the Secret Manager tool, you can associate app secrets with a specific project and share them across multiple projects.
|
|
|
|
|
The Secret Manager tool stores sensitive data during the development of an ASP.NET Core project. In this context, a piece of sensitive data is an app secret. App secrets are stored in a separate location from the project tree. The app secrets are associated with a specific project or shared across several projects. The app secrets aren't checked into source control.
|
|
|
|
|
|
|
|
|
|
>[!WARNING]
|
|
|
|
|
> [!WARNING]
|
|
|
|
|
> The Secret Manager tool doesn't encrypt the stored secrets and shouldn't be treated as a trusted store. It's for development purposes only. The keys and values are stored in a JSON configuration file in the user profile directory.
|
|
|
|
|
|
|
|
|
|
## Installing the Secret Manager tool
|
|
|
|
|
## How the Secret Manager tool works
|
|
|
|
|
|
|
|
|
|
# [Visual Studio](#tab/visual-studio/)
|
|
|
|
|
The Secret Manager tool abstracts away the implementation details, such as where and how the values are stored. You can use the tool without knowing these implementation details. The values are stored in a [JSON](https://json.org/) configuration file in a system-protected user profile folder on the local machine:
|
|
|
|
|
|
|
|
|
|
Right-click the project in Solution Explorer, and select **Edit \<project_name\>.csproj** from the context menu.
|
|
|
|
|
Add the highlighted line to the *.csproj* file, and save to restore the associated NuGet package:
|
|
|
|
|
* Windows: `%APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secrets.json`
|
|
|
|
|
* Linux & macOS: `~/.microsoft/usersecrets/<user_secrets_id>/secrets.json`
|
|
|
|
|
|
|
|
|
|
[!code-xml[](app-secrets/sample/UserSecrets/UserSecrets-before.csproj?highlight=10)]
|
|
|
|
|
In the preceding file paths, replace `<user_secrets_id>` with the `UserSecretsId` value specified in the *.csproj* file.
|
|
|
|
|
|
|
|
|
|
Right-click the project in Solution Explorer again, and select **Manage User Secrets** from the context menu. This gesture adds a new `UserSecretsId` node within a `PropertyGroup` of the *.csproj* file, as highlighted in the following sample:
|
|
|
|
|
Don't write code that depends on the location or format of data saved with the Secret Manager tool. These implementation details may change. For example, the secret values aren't encrypted, but could be in the future.
|
|
|
|
|
|
|
|
|
|
[!code-xml[](app-secrets/sample/UserSecrets/UserSecrets-after.csproj?highlight=4)]
|
|
|
|
|
::: moniker range="<= aspnetcore-2.0"
|
|
|
|
|
## Install the Secret Manager tool
|
|
|
|
|
|
|
|
|
|
Saving the modified *.csproj* file also opens a `secrets.json` file in the text editor. Replace the contents of the `secrets.json` file with the following code:
|
|
|
|
|
The Secret Manager tool is bundled with the .NET Core CLI in .NET Core SDK 2.1. For .NET Core SDK 2.0 and earlier, tool installation is necessary.
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"MySecret": "ValueOfMySecret"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
Install the [Microsoft.Extensions.SecretManager.Tools](https://www.nuget.org/packages/Microsoft.Extensions.SecretManager.Tools/) NuGet package in your ASP.NET Core project:
|
|
|
|
|
|
|
|
|
|
# [Visual Studio Code](#tab/visual-studio-code/)
|
|
|
|
|
[!code-xml[](app-secrets/samples/1.1/UserSecrets/UserSecrets.csproj?name=snippet_CsprojFile&highlight=13-14)]
|
|
|
|
|
|
|
|
|
|
Add `Microsoft.Extensions.SecretManager.Tools` to the *.csproj* file and run [dotnet restore](/dotnet/core/tools/dotnet-restore). You can use the same steps to install the Secret Manager Tool using for the command line.
|
|
|
|
|
|
|
|
|
|
[!code-xml[](app-secrets/sample/UserSecrets/UserSecrets-before.csproj?highlight=10)]
|
|
|
|
|
|
|
|
|
|
Test the Secret Manager tool by running the following command:
|
|
|
|
|
Execute the following command in a command shell to validate the tool installation:
|
|
|
|
|
|
|
|
|
|
```console
|
|
|
|
|
dotnet user-secrets -h
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The Secret Manager tool will display usage, options and command help.
|
|
|
|
|
The Secret Manager tool displays sample usage, options, and command help:
|
|
|
|
|
|
|
|
|
|
```console
|
|
|
|
|
Usage: dotnet user-secrets [options] [command]
|
|
|
|
|
|
|
|
|
|
Options:
|
|
|
|
|
-?|-h|--help Show help information
|
|
|
|
|
--version Show version information
|
|
|
|
|
-v|--verbose Show verbose output
|
|
|
|
|
-p|--project <PROJECT> Path to project. Defaults to searching the current directory.
|
|
|
|
|
-c|--configuration <CONFIGURATION> The project configuration to use. Defaults to 'Debug'.
|
|
|
|
|
--id The user secret ID to use.
|
|
|
|
|
|
|
|
|
|
Commands:
|
|
|
|
|
clear Deletes all the application secrets
|
|
|
|
|
list Lists all the application secrets
|
|
|
|
|
remove Removes the specified user secret
|
|
|
|
|
set Sets the user secret to the specified value
|
|
|
|
|
|
|
|
|
|
Use "dotnet user-secrets [command] --help" for more information about a command.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> [!NOTE]
|
|
|
|
|
> You must be in the same directory as the *.csproj* file to run tools defined in the *.csproj* file's `DotNetCliToolReference` nodes.
|
|
|
|
|
> You must be in the same directory as the *.csproj* file to run tools defined in the *.csproj* file's `DotNetCliToolReference` elements.
|
|
|
|
|
::: moniker-end
|
|
|
|
|
|
|
|
|
|
The Secret Manager tool operates on project-specific configuration settings that are stored in your user profile. To use user secrets, the project must specify a `UserSecretsId` value in its *.csproj* file. The value of `UserSecretsId` is arbitrary, but is generally unique to the project. Developers typically generate a GUID for the `UserSecretsId`.
|
|
|
|
|
## Set a secret
|
|
|
|
|
|
|
|
|
|
Add a `UserSecretsId` for your project in the *.csproj* file:
|
|
|
|
|
The Secret Manager tool operates on project-specific configuration settings stored in your user profile. To use user secrets, define a `UserSecretsId` element within a `PropertyGroup` of the *.csproj* file. The value of `UserSecretsId` is arbitrary, but is unique to the project. Developers typically generate a GUID for the `UserSecretsId`.
|
|
|
|
|
|
|
|
|
|
[!code-xml[](app-secrets/sample/UserSecrets/UserSecrets-after.csproj?highlight=4)]
|
|
|
|
|
::: moniker range="<= aspnetcore-1.1"
|
|
|
|
|
[!code-xml[](app-secrets/samples/1.1/UserSecrets/UserSecrets.csproj?name=snippet_PropertyGroup&highlight=3)]
|
|
|
|
|
::: moniker-end
|
|
|
|
|
::: moniker range=">= aspnetcore-2.0"
|
|
|
|
|
[!code-xml[](app-secrets/samples/2.1/UserSecrets/UserSecrets.csproj?name=snippet_PropertyGroup&highlight=3)]
|
|
|
|
|
::: moniker-end
|
|
|
|
|
|
|
|
|
|
Use the Secret Manager tool to set a secret. For example, in a command window from the project directory, enter the following:
|
|
|
|
|
> [!TIP]
|
|
|
|
|
> 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 *.csproj* file. Visual Studio 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:
|
|
|
|
|
> [!INCLUDE[secrets.json file](~/includes/app-secrets/secrets-json-file.md)]
|
|
|
|
|
|
|
|
|
|
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 *.csproj* file exists:
|
|
|
|
|
|
|
|
|
|
```console
|
|
|
|
|
dotnet user-secrets set MySecret ValueOfMySecret
|
|
|
|
|
dotnet user-secrets set "Movies:ServiceApiKey" "12345"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can run the Secret Manager tool from other directories, but you must use the `--project` option to pass in the path to the *.csproj* file:
|
|
|
|
|
In the preceding example, the colon denotes that `Movies` is an object literal with a `ServiceApiKey` property.
|
|
|
|
|
|
|
|
|
|
The Secret Manager tool can be used from other directories too. Use the `--project` option to supply the file system path at which the *.csproj* file exists. For example:
|
|
|
|
|
|
|
|
|
|
```console
|
|
|
|
|
dotnet user-secrets set MySecret ValueOfMySecret --project c:\work\WebApp1\src\webapp1
|
|
|
|
|
dotnet user-secrets set "Movies:ServiceApiKey" "12345" --project "C:\apps\WebApp1\src\WebApp1"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can also use the Secret Manager tool to list, remove and clear app secrets.
|
|
|
|
|
## 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 on Windows:
|
|
|
|
|
|
|
|
|
|
## Accessing user secrets via configuration
|
|
|
|
|
```console
|
|
|
|
|
type .\input.json | dotnet user-secrets set
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You access Secret Manager secrets through the configuration system. Add the `Microsoft.Extensions.Configuration.UserSecrets` package and run [dotnet restore](/dotnet/core/tools/dotnet-restore).
|
|
|
|
|
Use the following command on macOS and Linux:
|
|
|
|
|
|
|
|
|
|
Add the user secrets configuration source to the `Startup` method:
|
|
|
|
|
```console
|
|
|
|
|
cat ./input.json | dotnet user-secrets set
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
[!code-csharp[](app-secrets/sample/UserSecrets/Startup.cs?highlight=16-19)]
|
|
|
|
|
## Access a secret
|
|
|
|
|
|
|
|
|
|
You can access user secrets via the configuration API:
|
|
|
|
|
The [ASP.NET Core Configuration API](xref:fundamentals/configuration/index) provides access to Secret Manager secrets. If targeting .NET Core 1.x or .NET Framework, install the [Microsoft.Extensions.Configuration.UserSecrets](https://www.nuget.org/packages/Microsoft.Extensions.Configuration.UserSecrets) NuGet package.
|
|
|
|
|
|
|
|
|
|
[!code-csharp[](app-secrets/sample/UserSecrets/Startup.cs?highlight=26-29)]
|
|
|
|
|
::: moniker range="<= aspnetcore-1.1"
|
|
|
|
|
Add the user secrets configuration source to the `Startup` constructor:
|
|
|
|
|
|
|
|
|
|
## How the Secret Manager tool works
|
|
|
|
|
[!code-csharp[](app-secrets/samples/1.1/UserSecrets/Startup.cs?name=snippet_StartupConstructor&highlight=5-8)]
|
|
|
|
|
::: moniker-end
|
|
|
|
|
|
|
|
|
|
The Secret Manager tool abstracts away the implementation details, such as where and how the values are stored. You can use the tool without knowing these implementation details. In the current version, the values are stored in a [JSON](http://json.org/) configuration file in the user profile directory:
|
|
|
|
|
User secrets can be retrieved via the `Configuration` API:
|
|
|
|
|
|
|
|
|
|
* Windows: `%APPDATA%\microsoft\UserSecrets\<userSecretsId>\secrets.json`
|
|
|
|
|
::: moniker range="<= aspnetcore-1.1"
|
|
|
|
|
[!code-csharp[](app-secrets/samples/1.1/UserSecrets/Startup.cs?name=snippet_StartupClass&highlight=23)]
|
|
|
|
|
::: moniker-end
|
|
|
|
|
::: moniker range=">= aspnetcore-2.0"
|
|
|
|
|
[!code-csharp[](app-secrets/samples/2.1/UserSecrets/Startup.cs?name=snippet_StartupClass&highlight=14)]
|
|
|
|
|
::: moniker-end
|
|
|
|
|
|
|
|
|
|
* Linux: `~/.microsoft/usersecrets/<userSecretsId>/secrets.json`
|
|
|
|
|
## String replacement with secrets
|
|
|
|
|
|
|
|
|
|
* macOS: `~/.microsoft/usersecrets/<userSecretsId>/secrets.json`
|
|
|
|
|
Storing passwords in plain text is risky. For example, a database connection string stored in *appsettings.json* may include a password for the specified user:
|
|
|
|
|
|
|
|
|
|
The value of `userSecretsId` comes from the value specified in *.csproj* file.
|
|
|
|
|
[!code-json[](app-secrets/samples/2.1/UserSecrets/appsettings-unsecure.json?highlight=3)]
|
|
|
|
|
|
|
|
|
|
You shouldn't write code that depends on the location or format of the data saved with the Secret Manager tool, as these implementation details might change. For example, the secret values are currently *not* encrypted today, but could be someday.
|
|
|
|
|
A more secure approach is to store the password as a secret. For example:
|
|
|
|
|
|
|
|
|
|
```console
|
|
|
|
|
dotnet user-secrets set "DbPassword" "pass123"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Replace the password in *appsettings.json* with a placeholder. In the following example, `{0}` is used as the placeholder to form a [Composite Format String](/dotnet/standard/base-types/composite-formatting#composite-format-string).
|
|
|
|
|
|
|
|
|
|
[!code-json[](app-secrets/samples/2.1/UserSecrets/appsettings.json?highlight=3)]
|
|
|
|
|
|
|
|
|
|
The secret's value can be injected into the placeholder to complete the connection string:
|
|
|
|
|
|
|
|
|
|
::: moniker range="<= aspnetcore-1.1"
|
|
|
|
|
[!code-csharp[](app-secrets/samples/1.1/UserSecrets/Startup2.cs?name=snippet_StartupClass&highlight=23-25)]
|
|
|
|
|
::: moniker-end
|
|
|
|
|
::: moniker range=">= aspnetcore-2.0"
|
|
|
|
|
[!code-csharp[](app-secrets/samples/2.1/UserSecrets/Startup2.cs?name=snippet_StartupClass&highlight=14-16)]
|
|
|
|
|
::: moniker-end
|
|
|
|
|
|
|
|
|
|
## List the secrets
|
|
|
|
|
|
|
|
|
|
[!INCLUDE[secrets.json file](~/includes/app-secrets/secrets-json-file-and-text.md)]
|
|
|
|
|
|
|
|
|
|
Run the following command from the directory in which the *.csproj* file exists:
|
|
|
|
|
|
|
|
|
|
```console
|
|
|
|
|
dotnet user-secrets list
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The following output appears:
|
|
|
|
|
|
|
|
|
|
```console
|
|
|
|
|
Movies:ServiceApiKey = 12345
|
|
|
|
|
Movies:ConnectionString = Server=(localdb)\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
In the preceding example, a colon in the key names denotes the object hierarchy within *secrets.json*.
|
|
|
|
|
|
|
|
|
|
## Remove a single secret
|
|
|
|
|
|
|
|
|
|
[!INCLUDE[secrets.json file](~/includes/app-secrets/secrets-json-file-and-text.md)]
|
|
|
|
|
|
|
|
|
|
Run the following command from the directory in which the *.csproj* file exists:
|
|
|
|
|
|
|
|
|
|
```console
|
|
|
|
|
dotnet user-secrets remove "Movies:ConnectionString"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The app's *secrets.json* file was modified to remove the key-value pair associated with the `MoviesConnectionString` key:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"Movies": {
|
|
|
|
|
"ServiceApiKey": "12345"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Running `dotnet user-secrets list` displays the following message:
|
|
|
|
|
|
|
|
|
|
```console
|
|
|
|
|
Movies:ServiceApiKey = 12345
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Remove all secrets
|
|
|
|
|
|
|
|
|
|
[!INCLUDE[secrets.json file](~/includes/app-secrets/secrets-json-file-and-text.md)]
|
|
|
|
|
|
|
|
|
|
Run the following command from the directory in which the *.csproj* file exists:
|
|
|
|
|
|
|
|
|
|
```console
|
|
|
|
|
dotnet user-secrets clear
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
All user secrets for the app have been deleted from the *secrets.json* file:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Running `dotnet user-secrets list` displays the following message:
|
|
|
|
|
|
|
|
|
|
```console
|
|
|
|
|
No secrets configured for this application.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Additional resources
|
|
|
|
|
|
|
|
|
|
* [Configuration](xref:fundamentals/configuration/index)
|
|
|
|
|
* <xref:fundamentals/configuration/index>
|
|
|
|
|
* <xref:security/key-vault-configuration>
|