Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Encrypted configuration in ASP.NET Core

With web.config going away, what is the preferred way to store sensitive info (passwords, tokens) in the configurations of a web app built using ASP.NET Core?

Is there a way to automatically get encrypted configuration sections in appsettings.json?

like image 945
Ovi Avatar asked Mar 17 '16 13:03

Ovi


People also ask

What is encryption configuration?

You store configuration data and sensitive network information in configuration files. Encrypting configuration files enables you to secure the information they store. Decrypting means disabling the encryption of configuration files on a device and making the files readable to all.

What is protected configuration in asp net?

You can use protected configuration to encrypt sensitive information, including user names and passwords, database connection strings, and encryption keys, in a Web application configuration file such as the Web. config file.


2 Answers

User secrets looks like a good solution for storing passwords, and, generally, application secrets, at least during development.

Check the official Microsoft documentation. You can also review this other SO question.

This is just a way to "hide" your secrets during development process and to avoid disclosing them into the source tree; the Secret Manager tool does not encrypt the stored secrets and should not be treated as a trusted store.

If you want to bring an encrypted appsettings.json to production, you can do so by building a custom configuration provider.

For example:

public class CustomConfigProvider : ConfigurationProvider, IConfigurationSource {     public CustomConfigProvider()     {     }      public override void Load()     {         Data = UnencryptMyConfiguration();     }      private IDictionary<string, string> UnencryptMyConfiguration()     {         // do whatever you need to do here, for example load the file and unencrypt key by key         //Like:        var configValues = new Dictionary<string, string>        {             {"key1", "unencryptedValue1"},             {"key2", "unencryptedValue2"}        };        return configValues;     }      private IDictionary<string, string> CreateAndSaveDefaultValues(IDictionary<string, string> defaultDictionary)     {         var configValues = new Dictionary<string, string>         {             {"key1", "encryptedValue1"},             {"key2", "encryptedValue2"}         };         return configValues;                     }      public IConfigurationProvider Build(IConfigurationBuilder builder)     {        return new CustomConfigProvider();     } } 

Define a static class for your extension method:

public static class CustomConfigProviderExtensions {                       public static IConfigurationBuilder AddEncryptedProvider(this IConfigurationBuilder builder)         {             return builder.Add(new CustomConfigProvider());         } } 

And then you can activate it:

// Set up configuration sources. var builder = new ConfigurationBuilder()     .AddJsonFile("appsettings.json")     .AddEncryptedProvider()     .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); 
like image 165
Luca Ghersi Avatar answered Sep 24 '22 10:09

Luca Ghersi


I agree with @CoderSteve that writing a whole new provider is too much work. It also doesn't build on the existing standard JSON architecture. Here is a solution that I come up with the builds on top of the standard JSON architecture, uses the preferred .Net Core encryption libraries, and is very DI friendly.

public static class IServiceCollectionExtensions {     public static IServiceCollection AddProtectedConfiguration(this IServiceCollection services)     {         services             .AddDataProtection()             .PersistKeysToFileSystem(new DirectoryInfo(@"c:\keys"))             .ProtectKeysWithDpapi();          return services;     }      public static IServiceCollection ConfigureProtected<TOptions>(this IServiceCollection services, IConfigurationSection section) where TOptions: class, new()     {         return services.AddSingleton(provider =>         {             var dataProtectionProvider = provider.GetRequiredService<IDataProtectionProvider>();             section = new ProtectedConfigurationSection(dataProtectionProvider, section);              var options = section.Get<TOptions>();             return Options.Create(options);         });     }      private class ProtectedConfigurationSection : IConfigurationSection     {         private readonly IDataProtectionProvider _dataProtectionProvider;         private readonly IConfigurationSection _section;         private readonly Lazy<IDataProtector> _protector;          public ProtectedConfigurationSection(             IDataProtectionProvider dataProtectionProvider,             IConfigurationSection section)         {             _dataProtectionProvider = dataProtectionProvider;             _section = section;              _protector = new Lazy<IDataProtector>(() => dataProtectionProvider.CreateProtector(section.Path));         }          public IConfigurationSection GetSection(string key)         {             return new ProtectedConfigurationSection(_dataProtectionProvider, _section.GetSection(key));         }          public IEnumerable<IConfigurationSection> GetChildren()         {             return _section.GetChildren()                 .Select(x => new ProtectedConfigurationSection(_dataProtectionProvider, x));         }          public IChangeToken GetReloadToken()         {             return _section.GetReloadToken();         }          public string this[string key]         {             get => GetProtectedValue(_section[key]);             set => _section[key] = _protector.Value.Protect(value);         }          public string Key => _section.Key;         public string Path => _section.Path;          public string Value         {             get => GetProtectedValue(_section.Value);             set => _section.Value = _protector.Value.Protect(value);         }          private string GetProtectedValue(string value)         {             if (value == null)                 return null;              return _protector.Value.Unprotect(value);         }     } } 

Wire up your protected config sections like this:

public void ConfigureServices(IServiceCollection services) {     services.AddMvc();      // Configure normal config settings     services.Configure<MySettings>(Configuration.GetSection("MySettings"));      // Configure protected config settings     services.AddProtectedConfiguration();     services.ConfigureProtected<MyProtectedSettings>(Configuration.GetSection("MyProtectedSettings")); } 

You can easily create encrypted values for your config files using a controller like this:

[Route("encrypt"), HttpGet, HttpPost] public string Encrypt(string section, string value) {     var protector = _dataProtectionProvider.CreateProtector(section);     return protector.Protect(value); } 

Usage: http://localhost/cryptography/encrypt?section=SectionName:KeyName&value=PlainTextValue

like image 21
Scott Roberts Avatar answered Sep 21 '22 10:09

Scott Roberts