SecureStore is our open-source (MIT-licensed) solution to secrets management for .NET developers. It’s intended to be dead simple and boldly embraces the KISS principle. We’ve been using it in production for a while now (years, actually!), but hadn’t gotten around to officially releasing it despite its public availability on our GitHub page.
What is it?
SecureStore is a library for .NET developers that is “the correct way” to avoid storing passwords in your codebase. Imagine the following code:
AWS.S3.GetExpringUrl("s3://bucket.foo/bar", "my aws access id", "my aws access key");
It would be a colossal mistake to commit this line of code to your source code repository (GIT, Mercurial, SVN, etc.) even if only trusted members of your team have access to the code.
With SecureStore, that line becomes
AWS.S3.GetExpiringUrl("s3://bucket.foo/bar", "my aws access id", sman.Retrieve("awskey"));
How does it work?
Passwords are encrypted and stored in a commit-friendly1 plaintext (JSON) file that can be safely redistributed, committed, or accessed by anyone on the team. A separate key file is used which should remain secret, and needs to be copied to the production servers “out-of-band” via an existing secure channel.
Who is it for?
SecureStore is primarily targeting .NET developers who write code in one environment, then ship it to production in another, secure location. Typically, this would mean ASP.NET or other .NET web developers, but other .NET developers could also benefit from this.
Best use case?
What we do at NeoSmart is generate two pairs of key/password files, one for development and one for production. Both encrypted password files are committed to our GIT repository, and developers at NeoSmart are given a copy of the dev key file (the path to which is included in .gitignore so it is never accidentally committed to the repository). The startup code in our web app then looks like this:
#if DEBUG SecretsManager sman = SecretsManager.LoadStore("./secrets/dev.secrets"); sman.LoadKeyFromFile("./secrets/dev.key"); #else SecretsManager sman = SecretsManager.LoadStore("./secrets/production.secrets"); sman.LoadKeyFromFile("./secrets/production.key"); #endif S3.UserName = sman.Retrieve("UserName"); S3.Password = sman.Retrieve("Password"); //rinse and repeat
There’s of course no need to store the username in the encrypted secrets vault, but doing so makes the code a lot simpler to follow.
Why use a secrets manager at all?
It’s true that you can avoid using a secrets manager and still retain security if you keep your passwords out of the source code repository and securely copy them to the production (servers), but there are real, tangible benefits to using a secrets manager instead:
- Copy the secret once, update the passwords indefinitelyIf you were to store the passwords in a plain text file, each time a password or key is added/changed/removed you would need to manually (and out-of-band) copy the changed passwords file to your production server. With a simple secrets manager like SecureStore, you copy the key file to the server once when you initialize a production server, and thereafter you can transmit new passwords to the server however you would like – on the front page of the New York Times, if you like. They’re safely encrypted.
- Passwords should be part of your commit history, just not human-readableIf you want to test your code from a year ago to hunt down a bug, what do you do when you used services that you don’t use any more? How do you roll back your code to the way it was last week when the password file or its contents have changed since then? A secrets manager lets you embrace the benefits of version control for your passwords without sacrificing your security.
- It makes switching between development and production secrets easierSee above.
- Your passwords are encrypted at restYou can store the encryption key used by SecureStore in any way you like, even on a physically separate device. You can retrieve it from another server, set it via an environment variable, etc. in whatever method best suits your and your company’s needs. The integrity of your company and the security of your secrets is not tied to how you deploy your passwords, meaning you can make password deployment easy but key deployment hard without feeling the grind.
- I’m sure you can come up with more reasons why using a simple secrets manager like SecureStore is a better idea than using plain text. It’s also a better idea than using an overly-complicated secrets manager that ties you down to a specific deployment strategy, requires the use of a separate secrets server, needs docker for deployment, etc.
Further documentation and a proper readme.md are forthcoming.
Special care has been taken to balance security and commitability. The JSON contents of the password file are sorted alphabetically by key name, and the encoded, encrypted password is only re-generated if changed – otherwise the (IV, encrypted payload) tuple remains unchanged so that unnecessary changes aren’t committed to the codebase each time the password file is generated. ↩