How can I secure passwords stored inside web.config?

75,557

Solution 1

Generally, web.config is a secure file and IIS does not serve it, therefore it will not be exposed to users who are making requests to web server. Web server only serves specific type of files and web.config is surely not one of 'em.

Quite often you save your database connection string in it which includes password. Now imagine an scenario where web.config was not secure. You have created a major security threat to your application.

Therefore, specially as long as your project is not too big, you should not be worrying about it.

Yet, you may have a better approach but creating a project called "Resources" and save all the critical information such as settings, consts, enums and etc in there. That would be a slick and organized approach.

If you are passing the username/password over the wire though (for example in case of a secured API call), you may want to use https to make sure that information that are travelling are encrypted but that has nothing to do with the security of web.config file.

Solution 2

You can encrypt the web.config with aspnet_regiis. This is to stop people with access to your server from reading sensitive information.

By the way, I would put your config settings inside a class, that can then be injected into your controllers - it will make unit testing easier.

Solution 3

Why use Web.config?

One of the advantages of having data in Web.config as opposed to storing it in Constants, Enums, etc... is that you can change the data for different environments easily.

Protecting data

The best way to secure the data in Web.config is to encrypt them. Instead of encrypting entire sections in the config file as suggested by Joe and user1089766 you can just encrypt the password string and store it in the config.

Retrieving data

You can use a helper function such as the one below to decrypt the keys.

private const readonly string decryptKey = "";
public string GetFromConfig(string key)
{
   var encryptedText = System.Web.Configuration.WebConfigurationManager.AppSettings[key];
   var plainText = Decrypt(encryptedText, decryptKey);
   return plainText;
}

In this way the decryption key will be inside the project common for all environments. But you can change the data in the web.config easily without recompiling your app.

PS: You can change the decryptionKey and the corresponding data with each version to improve security.

Solution 4

The web.config file is just a file on the file system and so you should (mostly) consider its security in the same way as you would any other file. It will not be served by IIS (unless you make a frankly insane config change to IIS - easy to check for, just try and request it with a browser)

It is good practice to secure your website directory (c:/sites/my-site-here or whatever) using folder permissions to just the website app domain user to read the files (and the deployment user if needed) by using normal windows file permissions

So it may not be necessary to encrypt if you have confidence in the security of the web server. However if you are say on shared hosting, or selling the website code, or the source code is public, then it might be wise to encrypt it. it is a little bit hassle some though.

Solution 5

I know this may sound like over engineering, but if you can, you really should use a secret management service, such as AWS Secrets Manager or Azure Key Vault.

By storing your passwords, even in encrypted form, in your web.config or app.config, you creating several problems for yourself:

  • now your encrypted passwords are going to be committed to your source control, making them accessible to anyone with the read access. To get rid of them properly, you'll need to do a history rewrite (if you are using git) which is a major pain
  • while you technically can put your passwords in the machine-level config, and outside of the source control, you'll need to update all those files when your password changes
  • anyone who's got your encrypted password now can try to decrypt it, using either aspnet_regiis.exe tool (if that's what you used to encrypt it), or if you rolled your own security, reverse engineer that using your source code, figuring out what decryption algo & key it is using
  • whenever you need to change a password, you'll need to make changes to that file & re-deploy the application.

On the other hand, when you use a secret management service, no secrets or decryption key or algorithm is stored in your source code. Retrieving a secret is as simple as this:

For Azure Key Vault:

var keyVaultUrl = "https://<your-key-vault-name>.vault.azure.net/";
var credential =  new DefaultAzureCredential();    
var client = new SecretClient(vaultUri: new Uri(keyVaultUrl), credential);    
KeyVaultSecret secret = client.GetSecret("<your-secret-name>");    
Console.WriteLine($"{secret.Name}: {secret.Value}");

For AWS Secrets Manager (skipped some error handling code):

var client = new AmazonSecretsManagerClient(accessKeyId, secretAccessKey, 
                                            RegionEndpoint.APSoutheast2);
var request = new GetSecretValueRequest {
    SecretId = secretName
};
GetSecretValueResponse response = null;
response = client.GetSecretValueAsync(request).Result;

This approach has lots of advantages over storage of local secrets:

  • you don't have to mess with storing different values in configs for Prod/Staging/Dev environments -- just read appropriately named secrets (such as '[Dev|Prod|Stag]DBPassword`
  • only selected few people can have access to the very important secrects (such as, I dunno, say Transfer all $$$ from Deus account to all E-Coin wallets everywhere authorisation code)
  • if anyone steals your source code (disgruntled employee, accidental leak) non of your passwords have been leaked
  • changing a password is easy -- you just update it using the could management console and restart the app(s)

I have written a couple of articles, showing how to set up and read secrets with AWS and Azure on my blog, feel free to check it out if you need step-by-step directions and complete source code:

Share:
75,557
john Gu
Author by

john Gu

Updated on January 27, 2021

Comments

  • john Gu
    john Gu over 3 years

    I have added the following settings inside my web.config file to initiate an API call to external system. So I am storing the API URL + username + password as follows:-

    <appSettings>
        <add key="ApiURL" value="https://...../servlets/AssetServlet" />
        <add key="ApiUserName" value="tmsservice" />
        <add key="ApiPassword" value="test2test2" /> 
    

    Then inside my action method I will be referencing these values when building the web client as follows:-

    public ActionResult Create(RackJoin rj, FormCollection formValues)
            {
               XmlDocument doc = new XmlDocument();
               using (var client = new WebClient())
                    {
                        var query = HttpUtility.ParseQueryString(string.Empty);
                        foreach (string key in formValues)
                        {
                            query[key] = this.Request.Form[key];
                        }
    
                        query["username"] = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiUserName"];
                        query["password"] = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiPassword"];
    
                        string apiurl = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiURL"];
    

    But in this was I will be exposing the username and password and these can be captured by users, so my question is how I can secure the API username and password?