Best practices for private config data and connection strings in configuration in ASP.NET and Azure
A reader emailed asking how to avoid accidentally checking in passwords and other sensitive data into GitHub or source control in general. I think it's fair to say that we've all done this once or twice - it's a rite of passage for developers old and new.
The simplest way to avoid checking in passwords and/or connection strings into source control is to (no joke) keep passwords and connection strings out of your source.
Sounds condescending or funny, but it's not, it's true. You can't check in what doesn't exist on disk.
That said, sometimes you just need to mark a file as "ignored," meaning it's not under source control. For some systems that involves externalizing configuration values that may be in shared config files with a bunch of non-sensitive config data.
ASP.NET 4.6 secrets and connection strings
Just to be clear, how "secret" something is is up to you. If it's truly cryptographically secret or something like a private key, you should be looking at data protection systems or a Key Vault like Azure Key Vault. Here we are talking about medium business impact web apps with API keys for 3rd party web APIs and connection strings that can live in memory for short periods. Be smart.
ASP.NET 4.6 has web.config XML files like this with name/value pairs.
<appSettings>
<add key="name" value="someValue" />
<add key="name" value="someSECRETValue" />
</appSettings>
We don't want secrets in there! Instead, move them out like this:
<appSettings file="Web.SECRETS.config">
<add key="name" value="someValue" />
</appSettings>
Then you just put another appSettings section in that web.secrets.config file and it gets merged at runtime.
NOTE: It's worth pointing out that the AppSettings technique also works for Console apps with an app.config.
Finally, be sure to add Web.secrets.config (or, even better, make it *.secrets and use a unique extension to identify your sensitive config.
This externalizing of config also works with the <connectionStrings> section, except you use the configSource attribute like this:
<connectionStrings configSource="secretConnectionStrings.config">
</connectionStrings>
Connection Strings/App Secrets in Azure
When you're deploying a web app to Azure (as often these apps are deployed from source/GitHub, etc) you should NEVER put your connection strings or appSettings in web.config or hard code them.
Instead, always use the Application Settings configuration section of Web Apps in Azure.
These collection strings and name value pairs will automatically be made available transparently to your website so you don't need to change any ASP.NET code. Considered them to have more narrow scope than what's in web.config, and the system will merge the set automatically.
Additionally they are made available as Environment Variables, so you can Environment.GetEnvironmentVariable("APPSETTING_yourkey") as well. This works in any web framework, not just ASP.NET, so in PHP you just getenv('APPSETTING_yourkey") as you like.
The full list of database connection string types and the prepended string used for environment variables is below:
- If you select “Sql Databases”, the prepended string is “SQLAZURECONNSTR_”
- If you select “SQL Server” the prepended string is “SQLCONNSTR_”
- If you select “MySQL” the prepended string is “MYSQLCONNSTR_”
- If you select “Custom” the prepended string is “CUSTOMCONNSTR_”
ASP.NET 5
ASP.NET 5 has the concept of User Secrets or User-Level Secrets where the key/value pair does exist in a file BUT that file isn't in your project folder, it's stored in your OS user profile folder. That way there's no chance it'll get checked into source control. There's a secret manager (it's all beta so expect it to change) where you can set name/value pairs.
ASP.NET also has very flexible scoping rules in code. You can have an appSettings, then an environment-specific (dev, test, staging, prod) appSettings, then User Secrets, and then environment variables. All of this is done via code configuration and is, as I mentioned, deeply flexible. If you don't like it, you can change it.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsDevelopment())
{
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets();
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
So, in conclusion:
- Don't put private stuff in code.
- Seems obvious, but...
- Avoid putting private stuff in common config files
- Externalize them AND ignore the externalized file so they don't get checked in
- Consider using Environment Variables or User-level config options.
- Keep sensitive config out of your project folder at development time
I'm sure I missed something. What are YOUR tips, Dear Reader?
Resources
Image Copyright Shea Parikh - used under license from http://getcolorstock.com
Sponsor: Big thanks to Infragistics for sponsoring the blog this week! Responsive web design on any browser, any platform and any device with Infragistics jQuery/HTML5 Controls. Get super-charged performance with the world’s fastest HTML5 Grid - Download for free now!
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
@Mike: yes don't store your environment or secret data in web.config. This way you never have to transform your config when you promote your builds.
Of course you will need them in test or prod and everything in between. But those secrets should live on the server, which is hopefully better secured than your source control. So you set them when you deploy to the server (only the first time, then just don't override the settings unless you really, really need to). That way, if you accidentally create a public instead of a private github repo, no bad guy can see those secrets.
You'll need access during development, of course. But those configs should be passed around through a different way than storing them generally in your source control. Maybe even a different access code for each developer. That way you can monitor who does what.
http://stackoverflow.com/questions/6940004/asp-net-web-config-configsource-vs-file-attributes
We then use combination of PowerShell and TeamCity tasks to replace the configuration file at the time of deploying a package. The team city had two builds: One to just build the solution, other to deploy it to specific environment.
The advantage to this approach is that we always have one single build which propagates from SIT to UAT and PROD.
Thanks to you, now we know how to avoid this problem.
# Encrypt the appSettings section of web.config
& $env:WinDir\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis -pef "appSettings" "C:\inetpub\wwwroot"
# If later you need to decrypt that section, you can run
& $env:WinDir\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis -pdf "appSettings" "C:\inetpub\wwwroot"
ASP.NET IIS Registration Tool (Aspnet_regiis.exe)
"Finally, be sure to add Web.secrets.config (or, even better, make it *.secrets and use a unique extension to identify your sensitive config."
I was wondering is the support for configProtectionProvider being dropped entirely with the json file format?
.config files are exempt from downloading.
Good tips, though.
The most reasonable approach to me is base on the SOD (separation of duties) rules: sensitive settings in development environment are not protected beyond the infrastructure controls (access to source control system, intranet, etc), while sensitive settings in all other environments (QA, pre-prod, prod) are closely guarded by and known to a designated team (like systems engineers assigned to project). So, it's okay to have a connection string in plain text (or encrypted with a known secret) checked in as long as it points to the dev environment. This way it's easy for development team to share code and use common dev servers for dev testing and dev work. Once the code is out for deployment (in QA, pre-prod, prod), then all sensitive settings must be encrypted by a dedicated team members (specifically, non-developers) with the keys/passwords/certs that only they know (using aspnet_regiis, ciphesafe, or whatever tool or technique is approved by the corporate InfoSec policies). This may not be a preferred method for all projects, but it seems to be working better than anything else I've seen in the enterprise environment.
http://www.allkeysgenerator.com/Random/ASP-Net-MachineKey-Generator.aspx
But it does not help anything in case the "outside person" got access to the server/OS or even against inside persons that should not but accidentally have access to the file because of, for example, insufficient ACLs - because in this case the person can easily decrypt it just like the application that uses this file can.
I'm seeing this right, do I?
Time to update your footer to say 2016! try not hard coding to avoid updating every year.
Anyways, Happy new year.
P.S I love to read your blogs :)
I went straight to Azure KeyVault and haven't looked back. I store all connection strings, 3rd party API credentials, or anything else somewhat sensitive in KeyVault as a secret.
A little PowerShell gets it created and configured: https://azure.microsoft.com/en-gb/documentation/articles/key-vault-get-started/
A small KeyVault class added to the project template retrieves secrets, using sample code from here: https://github.com/Azure/azure-content/blob/master/articles/key-vault/key-vault-use-from-web-application.md
Then add secret1URL, secret2URL, etc. in the Azure Web App settings which point to the KeyVault URL's for each secret.
Now, there is nothing sensitive in the source code, repo, or Web App config settings.
The build config being chosen needs to be exposed to gulp and other task runners properly. (Real build config like debug, release etc.)
Use the built-in feature to transform the web/app config and know that it will happen every time you deploy a new version.
In the config file in your repository, do have URLs and logins to your dev environment but secure the dev environment do that it is not a big deal if the credentials are known.
Sometimes there are simply too many options for us to sift through at the early stages of a project.
Good post.
http://dusted.codes/aspnet-5-like-configuration-in-regular-dotnet-applications
There's no such thing like ASP.Net 4.6, the author confuses .Net 4.6 and ASP.Net 5.
I have employed another technique in projects where any sensitive or variable information would be replaced by a placeholder in the CONFIG. Its is this "template" config that we check into the source control.
We then have a utility that can be run against DEV, TST, STAGE, PROD environments that replaces the place holders with values based on the target environment.
The utility only stores non sensitive data in an XML file. Sensitive data like keys, passwords are to be keyed into the utility during deployment.
Comments would be appreciated if this technique can still be refined.
Thank you.
Sundip
Comments are closed.