How to securely store passwords in Visual Studio 2019 with Manage User Secrets Securely manage Database credentials, API Keys and other user secrets using ASP.NET Core Secret Manager and Visual Studio Manage User Secrets features

How to handle multipage TIFF files with ASP.NET C# (GDI+ alternative)

Being able to securely store database password and API keys in web applications while maintaining full efficiency in terms of debug and testing has always been a challenge for all developers.

Most ASP.NET developers used to store them in the <connectionStrings> and/or <appSettings> sections of the project's  Web.config file until few years ago:

then they switched to the appsettings.json file with Visual Studio 2015 and the new project architecture brought by the ASP.NET Core revolution:

In terms of pure functionality this behavior works very well, because when we launch our web applications will automatically fetch the required credentials whenever they need them even if we run them in Debug mode, just like they would do in a production environment.

Such practice has always been very convenient because it also leverages the fact that ASP.NET allows to define multiple files for different environments:

  • The Web.config approach could rely to multiple configuration files (Web.Debug.config, Web.Release.config, and so on) that could be easily "merged" during the publishing phase using a highly-configurabile XSD Transformation feature;
  • The appsettings.json approach supports multiple configuration files as well (appsettings.Development.json, appsettings.Production.json and so on) that can be used to add or override the default settings for specific runtime environments using a cascade logic;

Unfortunately, none of these places are safe or secure: if we get used to put our valuable credentials in those plain text files, there's a high risk that we'll end up "accidentally" pushing them in a GitHub repository, with all the other developers being able to see and use them. For that very reason, such habit is widely considered a bad practice and - if we're still using it - we should definitely take the chance to get rid of it and start to handle our valuable secrets in a much better (and safer) way. But how we can do that without losing the effectiveness provided by the "good old" (and insecure) approach?

The purpose of this article is to answer this question in a good way, providing a great alternative to those bad practices that will still allow the developers to be able to debug their web applications with ease without compromising security.

Introducing Secrets Storage

Starting with .NET Core 2.x and Visual Studio 2019, Microsoft provided their developers with a new feature that can be used to store any secret (DB passwords, Api Keys, and so on) in a secure and effective way: such feature is called Secret Manager and is well documented in this official guide:

In a nutshell, the new feature creates a secrets.json file in the development machine's user folder (in a typical Windows environment, the \Users\UserName\AppData\Roaming\Microsoft\UserSecrets directory) that can be used to add or override elements to the standard appsettings.json files using the same syntax they already have. This is good for a number of reasons, including:

  • The secrets.json file cannot be accessed from remote users, such as those who could get the project from a GitHub repository, because it will be created in a local folder.
  • The secrets.json file cannot be accessed from local users, because it will be created in the developer's very own personal folder (which is inaccessible for other local users).
  • The secrets.json file will work right out of the box, basically extending the appsettings.json file without forcing us to write any secret there.

Such feature is a great alternative to the ENVIRONMENT VARIABLES approach, which is another workaround suggested by Microsoft in the above guide that I personally don't like as much (at least for development environments) because is much less flexible and straightforward.

Now that we've chosen our path, let's see how we can implement it.

Adding secrets from Visual Studio

Among the greatest aspects of the Secrets Storage feature is the fact that it can be used from within the Visual Studio GUI, which is arguably the best way to do. All we need to do is to right-click to the project's root folder from Solution Explorer and select the Manage User Secret options, as shown in the screenshot below:

How to securely store passwords in Visual Studio 2019 with Manage User Secrets

As soon as we select that option, Visual Studio will add a UserSecretsId element within a PropertyGroup of the project's .csproj file: by default, the inner text of UserSecretsId is a GUID: however, the text is arbitrary and can be changed at will, as long as it's unique to the project (unless you want more projects to share the same secrets):

Visual Studio will also use that GUID to generate an empty secrets.json file in the following folder:

C:\Users\UserName\AppData\Roaming\Microsoft\UserSecrets\<RANDOM_GUID>\secrets.json

And open it for us from within the GUI in edit mode.

We can then proceed to create our very own structure of user secrets that we don't want to share, just like we normally do with a typical appsettings.json file:

Adding secrets from the .NET CLI

The Secret Manager tool includes an init command in the .NET Core SDK 3.0.100 or later that can be used to activate the feature. To enable it, open a command-prompt, navigate to the project's root folder and then type the following:

Running this command has the same effect of selecting the Manage User Secrets option from the Visual Studio GUI.

Once done, we'll be able to add our secrets to the secrets.json file in the following way:

Such method is definitely viable, but if you're a Visual Studio user I strongly suggest to use the GUI because it gives a better representation of the JSON file and greatly helps to organize the secrets thanks to the highly-readable key/value tree structure provided by the JSON format.

Retrieve the secrets.json values

Once those values have been set - regardless of how we did that - we can retrieve them from our application using the standard IConfiguration interface, the same that we use to retrieve the standard appsettings.json values.

Here's a typical example of a Startup.cs file that fetches a connection string from the secrets.json file and feeds it to an Entity Framework's DbContext to allow it to connect to the database:

which could also be written in the following way:

Both of the above snippets will work out of the box, because the ASP.NET Core Configuration API provides access to the Secret Manager secrets by default: this means that the secrets.json configuration file will be automatically added in development mode when the project calls CreateDefaultBuilder (within the Program.cs file's CreateWebHostBuilder() method) to initialize a new instance of the host with preconfigured defaults. More specifically, the CreateDefaultBuilder will call AddUserSecrets when the EnvironmentName is set to Development.

It goes without saying that, in the unlikely event that we're not planning to use the CreateDefaultBuilder method, we can explicitly add the user secret configurations using the AddUserSecrets method from the Microsoft.Extensions.Configuration namespace.

Conclusion

That's it, at least for now: we sincerely hope that this extensive overview of the Secret Manager feature will help many ASP.NET developers that are looking for a way to store their user secrets in a secure and effective way.

This article is part of the ASP.NET Core 5 and Angular book, available as paperback and e-book.

About Ryan

IT Project Manager, Web Interface Architect and Lead Developer for many high-traffic web sites & services hosted in Italy and Europe. Since 2010 it's also a lead designer for many App and games for Android, iOS and Windows Phone mobile devices for a number of italian companies. Microsoft MVP for Development Technologies since 2018.

View all posts by Ryan

Leave a Reply

Your email address will not be published. Required fields are marked *


The reCAPTCHA verification period has expired. Please reload the page.

This site uses Akismet to reduce spam. Learn how your comment data is processed.