Home > Back-end >  How to encrypt/decrypt connection string in appsettings.json?
How to encrypt/decrypt connection string in appsettings.json?

Time:08-24

I am developing an ASP.Net Core Web API and while on dev environment we just write our connection string in plain text on the appsettings.json file. But now we are going to publish this API to our production server and to web (we will use FortiWeb to publish the API, not Azure), so we can't have the user credentials in plain text anymore.

I've been searching for methods to protect/encrypt my connection string, but most solutions use Azure Key Vault ou user secrets. I decided to follow this article to encrypt and decrypt the appsettings.json info, but it's kinda incomplete. I managed to create the classes described on the article, but I'm stuck and have no idea about how to use it. This is the first time I'm developing an API completely from scratch, and I'm totally newbie on info security.

Here's what I have so far:

On appsettings.json file:

{
  "ConnectionStrings": {
    "MyConnection": "Data Source=MyServerName;Initial Catalog=MyDb;Persist Security Info=True;User ID=MyUser;Password=MyPwd"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

On DecryptedConfiguration.cs class:

public class DecryptedConfiguration : ConfigurationProvider
{
    public ICryptoTransform Decryptor;
    private ICryptoTransform Encryptor;

    internal DecryptedConfiguration(byte[] Key, byte[] IV)
    {
        Aes aes = Aes.Create();
        Decryptor = aes.CreateDecryptor(Key, IV);
        Decryptor = aes.CreateDecryptor(Key, IV);
    }

    public override bool TryGet(string key, out string value)
    {
        if (base.TryGet(key, out value))
        {
            byte[] decryptedBytes = Convert.FromBase64String(value);
            byte[] textBytes = Decryptor.TransformFinalBlock(decryptedBytes, 0, decryptedBytes.Length);
            value = Encoding.Unicode.GetString(textBytes);
            return true;
        }
        return false;
    }

    public override void Set(string key, string value)
    {
        byte[] textBytes = Encoding.Unicode.GetBytes(value);
        byte[] decryptedBytes = Decryptor.TransformFinalBlock(textBytes, 0, textBytes.Length);
        base.Set(key, Convert.ToBase64String(decryptedBytes));
    }
}

On DecryptedConfigurationSource.cs class:

public class DecryptedConfigurationSource : IConfigurationSource
{
    private byte[] Key;
    private byte[] IV;

    public DecryptedConfigurationSource(byte[] Key, byte[] IV)
    {
        this.Key = Key;
        this.IV = IV;
    }

    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        return new DecryptedConfiguration(Key, IV);
    }
}

On Startup.cs class:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ...

    Aes aes = Aes.Create();
    byte[] key = aes.Key;
    byte[] iv = aes.IV;

    IConfigurationBuilder encryptingBuilder = new ConfigurationBuilder()
        .AddJsonFile("appsettings.json")
        .Add(new DecryptedConfigurationSource(key, iv))
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
    IConfiguration cfg = encryptingBuilder.Build();

    string ecryptedValue = cfg.GetValue<string>("ConnectionStrings:MyConnection");
}

CodePudding user response:

Just as a short hint, I could recommend storing the values in environment variables. This is a bit more secure than storing them plainly in text files as accessing environment variables requires higher priviledges than reading a file. For many companies this was enough solution if azure key vault or aws secret manager could not be used. Environment variables you can use both if you host your app as docker container or running on VM. Here you have some article on easy to use solution, though the article for production recommends using azure key vault https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-6.0&tabs=windows

As an alternative you may consider using Secret manager provider described in the article.

I would not recommend going by what article suggest because this involves storing somewhere permanently the encryption key (article mentiones you should not call Aes aes = Aes.Create(); because every time it is called new key is generated, you would need a permanent one). But to have it secured you have to load it from something secure like azure key vault, if you have it in code, someone could just dowlonad your assembly, decompile it, access the stored key and can on its own decrypt all your secrets. If my understanding is correct the solution proposed in the article is "security through obscurity" rather than actual solution - it only makes the access to secrets a bit harder but still fully managable as if you place your secrets in the appsettings.json.

  • Related