How to avoid storing secrets in the source code?

 
 
  • Gérald Barré

This post is part of the series 'Password management'. Be sure to check out the rest of the blog posts of the series!

There are lots of example of projects which contains secrets in the source code. A secret can be a password, a server configuration, tokens to connect to a server, a certificate, etc. You can search for "Remove password" on GitHub to find more than 400k commits. These secrets may be used for deploying the application or connecting to an external service.

Let's see different ways to avoid passwords in the code. Other ways are depending on the technologies you use.

#Secret variables in CI

If your secrets are used for deploying your application, for instance in a PowerShell script, you can use secrets variables. Every build system, such as Azure Pipeline, Jenkins, or Travis, provides a way to declare secret variables. These variables are exposed as environment variables or a custom syntax accessible during the pipeline. Also, the value of these variables is hidden from the logs.

Environment variables are accessible in batch files or applications. For instance, with Azure Pipeline:

Azure Pipeline - Secret variablesAzure Pipeline - Secret variables

YAML
steps:
- powershell: Write-Host "$(password)"

#Environment variables

You can define environment variables per user on your development machine. So, in the code, you just need to know the name of the variable, not its value. This way, your secrets stay out of the code. However, the value of the environment variables is not encrypted and is easily accessible from other applications. Therefore, this solution may be sufficient for development, but not for production.

#ASP.NET Core - User secrets

In ASP.NET Core, you can set your configuration from different locations: JSON files, XML files, environment variables, or whatever you want. During development, you may need to store sensitive data in these configuration files. To avoid doing that, you can use the Secret Manager. The Secret Manager tool stores sensitive data in a JSON file under %appdata%, so you are sure to not commit them. It works the same as the appsettings.json file at the root of your project. A configuration provider allows us to read these values and merge them with the project configuration files.

It also works using a command line

Shell
dotnet user-secrets set "MyApp:Password" "123456"

For more details, you can read the documentation: Safe storage of app secrets in development in ASP.NET Core. If you are using Visual Studio, you can also read this blog post: Managing Secrets in .NET CORE 2.0 Apps.

#Azure Key Vault / AWS Key Management Service / Google Cloud Key Management Service

The main cloud providers all have a secure way to manage secrets. These solutions provide a good way to store secrets for the application you host on their services. They also provide additional services such as an audit file to know who uses the secrets and when. This may help you detect an attack.

The way to use secrets is dependent on the service you use. In the case of Azure, you can easily integrate AKV with ASP.NET Core:

C#
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((context, config) =>
        {
            var builtConfig = config.Build();

            config.AddAzureKeyVault(
                $"https://{builtConfig["Vault"]}.vault.azure.net/",
                // The credentials are not needed if you use a Managed Service Identity https://learn.microsoft.com/en-us/samples/azure-samples/app-service-msi-keyvault-dotnet/keyvault-msi-appservice-sample/?WT.mc_id=DT-MVP-5003978
                builtConfig["ClientId"],
                builtConfig["ClientSecret"]);
        })
        .UseStartup<Startup>();

For any other kind of application, you can use the client directly:

#Docker secrets

Docker provides a way to use secrets in a container: docker secrets. It allows you to create secrets, and use these secrets in the containers. For security purposes, you have to authorize a container to use a secret. If you use docker, it may be a good way to store your secrets.

Shell
docker secret create sample Password
docker service create --name redis --secret sample image

Note that you can access the docker secrets easily from ASP.NET Core as part of the configuration:

C#
public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddEnvironmentVariables()
        .AddDockerSecrets();

    Configuration = builder.Build();
}

You can read more about docker secrets in the documentation.

#Avoid using passwords

The best way to avoid storing passwords in the code is to remove the need for passwords. This doesn't mean it isn't secure, it just uses another way to authenticate. Setting up a Single Sign-On (SSO) solution like the Windows authentication may help you reduce the number of passwords needed. For instance, you can use the Windows authentication to access a SQL Server database, or internal web services. This way, you do not need to configure anything on your machine. Everything works as soon as you clone the repository and it is secured. How could you have a better experience?

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub