Don't Share Your Secrets! (.NET CORE Secret Manager Tool)

I’ve been working on this million-dollar project of mine and wanted it to make the project open source. So, I made a repository on github and pushed my project. That’s where I almost made a big mistake. Since my project was data driven so there were some connection strings stuff in my web.config file,

Practically I would remove the connection strings and other credentials stuff out of my web.config file before I push it to the repository. But it was a big headache for me since I had to make that single change every time I wanted to push my code. That was the time when I was working on a MVC project in .NET 4.6 or something. At that time, I couldn’t find any solution and eventually I was forced to make my repository private. Only for this one silly little thing, can you believe it?

But its 2016 guys and .NET CORE came up with so many new things. One of them is the Secret Manager Tool. Let’s see how we can utilize this tool to solve the kind of problems I was facing back in the old days. Fire up your Visual Studio and create a ASP.NET CORE application.

If you are starting with the Empty template, then you will need a ASP.NET Configuration file which is just a replacement for the web.config. And thats where all the app releated configurations live. So add that appsettings.json file and you will end up with this,

As you can see, we have a default connection string already set up for us. But since it’s a local database connection string it has no password and username fields. But in real life projects it will have those cause you can't use local database when you are in production. And it would be very stupid of us to share our own connection string credentials with the whole world. So, We are going to remove that connection string from here and add a descriptive text instead. So that when someone sees our appsettings.json they have some good idea on what is missing.


{
  "ConnectionStrings": {
    "DefaultConnection": "Please configure your own connection string here."
  }
}

We can use the Manage user secrets option from visual studio to store the actual connection string against a key. And this is how you will do that, just right click on the project and chose Manage user secrets option. Doing that will open up a secrets.json file where we can store key and value pairs. Here is how I stored the default connection string with the key of DefaultConnection

.

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=_CHANGE_ME;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

We are now safe from exposing our connection string to the outside world. But in development mode, we have to get that connection string. In Startup.cs add this code


public IConfigurationRoot Configuration { get; set; }

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

    if (env.IsDevelopment())
    {
        builder.AddUserSecrets();
    }

    builder.AddEnvironmentVariables();
    Configuration = builder.Build();
}

This code reads everything from the appsettings.json file and builds a configuration root(like ConfigurationManager back in old days). Only thing special here is if you are in development mode then it will read the keys and the values from the secrets.jon file. If any of the keys from secrets.json matches any of the keys from appsettings.json it will get replaced with that secret value.

You can check if the value is actually getting replaced or not using the configuration root property. In the code given below we got the value of the given key (string literal in third braces) using the configuration root and wrote it in the response,


public void Configure(IApplicationBuilder app)
{
    app.Run(async (context) =>
    {
        await context.Response.WriteAsync("ConnectionString:" + Configuration.GetConnectionString("DefaultConnection"));
    });
}

Upon running the app, you will get this response in the browser,

But that’s not all. Till now we were managing secrets using Visual Studio. But what happens if you are not developing using Visual Studio. Well then you will have to use the Secret Manager Tool. To install the tool, you will need this package configured in the tools node inside the packages.json file,


"Microsoft.Extensions.SecretManager.Tools" : "1.0.0-preview2-final"

Now, open up a command prompt in the project folder and run this command to check if the tool is actually installed or not.


dotnet user-secrets -h

You can play around with the other commands if you like in your free time. But just for now lets see how you can store secrets using this very simple command


dotnet user-secrets set key value

Replace the key and the value with your actual key and value literals. For example, here I’ve a secret settings for facebook.consumerkey

dotnet user-secrets set facebook.consumerkey 123456

Running this command will store a new secret settings in the secrets.json.


{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=_CHANGE_ME;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "facebook.consumerkey": "123456"
}

So, this is all about managing secret for your app using .NET CORE Secret Manager Tool. Just a reminder though,

Secret Manager Tool doesn’t encrypt keys or values for your apps. It’s just a storage to store the secrets only. You don't have to worry about the secrets.jon file when you push your code on source control because it stays in OS's user AppData folder. So, you dont have to explicietly add the secrets.json file in .gitignore also.

In some cases you may need a key called userSecretsId in your packages.json file to work with Secret Manager Tool. In those cases just configure the key with an arbitrary but unique value. Here, for example I’ve used a generated GuId string with the application name at the front for uniqueness,


"userSecretsId": "secret-manager-tool-05fc3644-a91a-42a5-90ed-ab3f47e8e6"