Image by Rina Pitucci | Some Rights Reserved
It is just too easy to accidentally push confidential information up to a publicly hosted source repository such as Github. Also, when managing a project with multiple developers, it can become messy managing multiple configuration files between team members.
How often do you pull the latest changes down from source control, and then need to reset a database connection string after someone else accidentally pushed their own modified App.config or Web.config file up?
Even when the settings or connection strings are not critically private, this can be a pain.
- Use configSource Attribute to move an Entire Configuration Section to Its Own File
- Use the File Attribute to Move Select Application Settings to an External File
- Add Special Files to .gitignore
- Documentation is KeyDocumentation is Key
Consider a typical Web.config file from an ASP.NET MVC web application (non-relevant content removed for clarity):
ASP.NET Web.config File Example:
In the above, there is a database connection string we likely don’t want to push to a public repo, and/or which may differ from developer to developer on a team, even internally if they are working against different or individual development versions of the application database.
Also, there is an email password, likely used to send email from within the application, which also may differ amongst team members during development, and which also should not be published publicly.
At the same time, there is a bunch of other stuff which is global to the application, so keeping the entire Web.config file out of source control is not an attractive option, either.
Fortunately, the .NET
ConfigurationManager affords us a couple of handy ways to deal with this.
Use configSource Attribute to move an Entire Configuration Section to Its Own File
We can use the
configSource attribute to move an entire Configuration Section to an external file. For example, database connection strings are one of the most common items we need to keep in our App.config or Web.config files, but which we also (usually) don’t want to publish to a publicly hosted source control repository.
We can add a separate configuration file named (for example) connectionStrings.config, and then use the
configSource attribute within our Web.config file to refer to it. To do so, add a new Web Configuration file, name it ConnectionStrings.config, and then put only the following in the new file (no xml header, nothing but the
<connectionStrings> section tags, and the
ConnectionStrings.config File Example:
<connectionStrings> <add name="DefaultConnection" value="YourConnectionStringAndPassword"/> </connectionStrings>
Then, we can modify our original Web.config file, removing the
<add> element from the
<connectionStrings> section, and instead, using the
configSource attribute to refer to the new
Modified Web.config File Using configSource:
<connectionStrings configSource="ConnectionStrings.config"> </connectionStrings>
Now, we can still access our connection string the same as always:
Accessing Connection String By Name:
var conn = ConfigurationManager.ConnectionStrings["DefaultConnection"]; string connString = conn.ConnectionString; // Etc...
In the above, accessing the connection string by name like that returns a
When we use the
configSource attribute, the Configuration Section to which it is applied can contain no actual elements. The entire section will be referred to from the external file. Note that the
configSource attribute can be used in this manner with any Configuration Section.
Use the File Attribute to Move Select Application Settings to an External File
You may have a case, such as our example We.config file above, in which most of the values in the
<appSettings> Configuration Section are global to the project, but also include a handful of settings which should remain private, and kept out of source control.
In these cases, there is a special
file attribute available specifically to the
<appSettings> section which essentially allows us to extend
<appSettings> to an external file. In other words, ConfigurationManager will recognize the contents in both locations when referring to
<appSettings> and make all transparently available within the application.
In our example case, we have an email password we would like to keep private. We might add another Web Configuration file named PrivateSettings.config. Once again, there should be no XML header. The only thing this file should contain will be a set of
<appSettings> elements, and within those, the special settings we wish to define privately.
Special PrivateSettings.config File Extends AppSettings Section:
<appSettings> <add key="MAIL_PASSWORD" value="xspbqmurkjadteck"/> </appSettings>
No, we remove the email password element from Web.config, and add the file attribute to the
<appSettings> section element, pointing to the new PrivateSettings.config file:
Add File Attribute to Web.config AppSettings:
Again, as before we can access any of our settings in the standard manner – externalizing the email password setting to a separate file is transparent to client code:
var pwd = ConfigurationManager.AppSettings["MAIL_PASSWORD"];
Add Special Files to .gitignore
Now we can add our Web.config file to source and commit, and add the two special files, ConnectionStrings.config and PrivateSettings.config to our .gitignore file, and commit away. When it’s time to push to a shared repo, our private information will stay private.
Documentation is Key
Of course, when we take this type of approach, it will be helpful to other developers if our documentation clearly indicates what is going on here. We might do this in our project README file, and/or add some XML comments at each point in our modified Web.config informing others that they will need to add the proper files to their local version of the project, and what those files should contain.
Additional Resources and Items of Interest
- Routing Basics in ASP.NET MVC
- Customizing Routes in ASP.NET MVC
- Creating a Clean, Minimal-Footprint ASP.NET WebAPI Project with VS 2012 and ASP.NET MVC 4
- ASP.NET MVC 5 Identity: Implementing Group-Based Permissions Management
I know this article is specifically addressing keeping sensitive data out of source control, but I wanted to point out that Microsoft recommends not using application settings at all, since they are vulnerable in the user’s files:
“Application settings has no built-in facility for encrypting information automatically. You should never store security-related information, such as database passwords, in clear text.”
If you follow Microsoft’s recommendation against putting sensitive data in XML at all, then the reason for splitting up the XML into non-committed and committed files changes. It’s no longer a security matter, but a developer convenience issue: If developers are altering settings to aid in their workflow, it helps to not have the file constantly showing up in the “changed files” list, so that’s why I would employ the techniques outlined in this article.
I’d still like to learn about best practices around sensitive data and developer workflows, if one chooses not to use app settings.
I’m working with C# library that runs as a background service. Problem is that I get warning messages when the connectionStrings.config file is not included in the project that more or less complain that the connectionString.config file is not copied to the output directory. When I include the connectionStrings.config into the project but exclude for GIT my builds break because the csproj lists a file that’s not present. How do I keep my secrets secret without breaking my CI builds??
I'm pretty sure the behavior would be the same as changing a value in the Web.config itself, but I'm not 100% certain.
If using this approach, does changing a value in an external file trigger an app pool recycle?
Create a private repo, with it's own branch maybe? Public repo = master, private contains dev branch(es)? Might take some fiddling. However, in general it might be better to keep the private stuff out, and everyone keep it local.
Is there any kind of middle ground where you are working in a repo that is public but with some other developers you trust and want to share those external files?
Some of the new devs may not know where to put the custom settings file. Maybe add a section that covers how to decide on the file’s location and why.