How do you encrypt a password within appsettings.json for ASP.net Core 2?

26,006

Solution 1

Remember do not store secrets in the main appsettings.json that is in the web site and usually held in source control. Use a file provider to locate the file in some other location elsewhere on the server.

If you have access to Azure, you could store the secret in Azure Key Vault instead of appsettings.json.

With that in mind, if your want to use a JSON file, you can use a bridge or a proxy class to handle the decryption of values.

First you will need a class to decrypt the values. For brevity, I won't go into the details of the decryption class here and will just assume that a class called SettingsDecryptor has been written and implements an interface called ISettingsDecryptor with a single method Decrypt which decrypts a string value.

The bridge class takes two constructor parameters.

  • The first is an IOptions<T> or IOptionsSnapshot<T> where T is that class that the section in appsettings.json is bound to via the services.Configure method (E.g. MyAppSettings). Alternatively, if you do not want to bind to a class, you could use IConfiguration instead and read directly from the configuration.
  • The second is the decryption class that implements ISettingsDecryptor.

In the bridge class, each property that requires decrypting should use the decryption class to decrypt the encrypted value in the configuration.

public class MyAppSettingsBridge : IAppSettings
{
    private readonly IOptions<MyAppSettings> _appSettings;

    private readonly ISettingsDecrypt _decryptor;

    public MyAppSettingsBridge(IOptionsSnapshot<MyAppSettings> appSettings, ISettingsDecrypt decryptor) {
        _appSettings = appSettings ?? throw new ArgumentNullException(nameof(appSettings));
        _decryptor = decryptor ?? throw new ArgumentException(nameof(decryptor));
    }

    public string ApplicationName => _appSettings.Value.ApplicationName;

    public string SqlConnectionSting => _decryptor.Decrypt(_appSettings.Value.Sql);

    public string OracleConnectionSting => _decryptor.Decrypt(_appSettings.Value.Oracle);
}

The DI container should be set up something like this:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddOptions();            
    services.Configure<MyAppSettings>(Configuration.GetSection("MyAppSettings"));
    services.AddSingleton(Configuration);        
    services.AddSingleton<ISettingsDecrypt, SettingsDecryptor>();
    services.AddScoped<IAppSettings, MyAppSettingsBridge>();
}

The controller can then have a constructor that takes the bridge as an IAppSettings to access the decrypted settings.

The above answer is a brief summary of the overall solution as there is quite a bit of code required.

The full detailed explanation can be seen at my blog post Hiding Secrets in appsettings.json – Using a Bridge in your ASP.Net Core Configuration (Part 4) where I describe using a bridge pattern in detail. There is also a full example (including a decryption class) on Github at https://github.com/configureappio/ConfiguarationBridgeCrypto

Solution 2

The JSON configuration provider does not support encryption. Currently, the only out of the box provider that does support encrypted configuration is Azure KeyVault. You can use KeyVault whether or not your application is actually hosted on Azure, and although it's not free, the allowances are such that it would likely only cost pennies in most scenarios.

That said, part of the beauty of Core is that it's completely modular. You can always create your own configuration provider(s) and implement whatever you want. For example, you could write a JSON provider that actually does support encryption, if that's how you want to go.

Solution 3

For ASP.NET Core the best solution is to do any transformations of the configuration values, like decryption or string replacements, when the application starts. This is why configuration provider exists.

The configuration providers can be chained. In the source code of the Microsoft.Extensions.Configuration there is class called ChainedConfigurationProvider that can be used as an example.

public static IHostBuilder CreateHostBuilder(string[] args)
{
    return new HostBuilder()
    .ConfigureAppConfiguration((host, config) => {

        var jsonFile = new ConfigurationBuilder();
        jsonFile.AddJsonFile("appsettings.json");
        // the json file is the source for the new configuration provider.
        config.AddConfiguration(jsonFile.Build());
    });
}

If you are using Docker Swarm or Kubernetes you don't have to encrypt the password in the appsettings.json file. You can use the build-in Key-per-file Configuration Provider or custom configuration provider to read the password from a docker secret and map it to a configuration value.

On my blog post How to manage passwords in ASP.NET Core configuration files I explain in detail how to create a custom configuration provider that allows you to keep only the password as a secret and update the configuration string at runtime. Also the the full source code of this article is hosted on github.com/gabihodoroaga/blog-app-secrets.

Solution 4

You could take a look at my Moonrise.StandardUtils.NetStd NuGet package. The Settings class has transparent en/decryption and is a much simpler way to access a JSON settings file - whether Application or User - in any .Net application.

Settings.Application.SettingsEncryptor = new DpApiSettingsEncryptor(DpApiSettingsEncryptor.ProtectionScope.Machine);
MyConfigClass config;
Settings.Application.Read("Configuration", ref config)

Anything individual or group of settings that has previously been encrypted will be decrypted. You can encrypt a setting with;

Settings.Application.Write("ContainerStartup:FileProviders:BrandingConfig", secretValue, true);

Or use the EncryptAppSettings.exe that is bundled into Moonrise.Microsoft.EncryptedJsonConfiguration or the Moonrise.Samples NuGet packages - you'll find it in the package file/directory - well it's a way to get an .exe to folk.

The Moonrise.Microsoft.EncryptedJsonConfiguration allows you to use the IConfigurationBuilder with .AddEncyptedJsonFile(...) which will then transparently decrypt any settings encrypted with the EncryptAppSettings.exe.

Here's a fragment of a partially encrypted appSettings.json

  "ContainerStartup": {
    "FileProviders": {
      "BrandingConfig": "[{ENC]{AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAsevacb5DdkaxvzOPPkLrdwQAAAACAAAAAAAQZgAAAAEAACAAAADITnvKp+Lnb5n6kPK7WyYuWFQLnbvbkOvgHBLBdIw2MAAAAAAOgAAAAAIAACAAAADqJZ0YUGC+jOEr4/6hgQ+8UdZ1ssbiEXXCjdhSV3teZ3AAAAAW4d8Z38JYNM1Dw45KquZYK+bTszYp/1wXt+LiYpiy2q88sOpQr5VpDFatgWar1aOePXA52RC6eZH1HFrYijqWTSEiffBqWzWZPPTXw1wkUVB5MLIjOq4bu33h+4Z23Vy+XaFsf6IFVl4ccM4fHpsRQAAAAAG5OP+nJQxzH3A7n3gnh8d2eAOFgLWzYCDgQon7NXHeEJcZezgxT+0npvIQ/kcYb1Xpwt7FiNtyJ2HZswL8MSg=]{ENC[{",
      "SearchingNotification": true,
      "UseBranding": false,
      "UseLocalModule": true,
      "UseLocalModuleEmbedded": true,
      "UseParentModules": true,
      "UseParentModulesEmbedded": false
    },
Share:
26,006
Ryan Battistone
Author by

Ryan Battistone

I'm a full-stack developer, writing web, mobile, desktop, and gaming applications in C# and JavaScript.

Updated on July 09, 2022

Comments

  • Ryan Battistone
    Ryan Battistone almost 2 years

    I'd like to use my appsettings.json to store a "master password".

    This master password would then be used to open up a private key (and its subsequent password store) generated by this excellent password store package: https://github.com/neosmart/SecureStore

    The problem is, I can't think of any way to encrypt the master password. I know in .NET 4.5, it was possible to do the following:

    1) Place your password into the web.config file

    2) Run this script: aspnet_regiis.exe -pef appSettings "C:\myfolder"

    3) Your password would end up being encrypted - but read securely by your program.

    https://www.codeproject.com/Articles/599416/Encrypting-ASP-NET-Application-Settings

    Am I going about this the right way or is there a better practice?