ASP.Net

ASP.NET MVC: Keep Private Settings Out of Source Control


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.

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:
<?xml version="1.0" encoding="utf-8"?>
<!--
  A bunch of ASP.NET MVC web config stuff goes here . . . 
  -->
<configuration>
  <connectionStrings>
    <add name="DefaultConnection" value="YourConnectionStringAndPassword"/>
  </connectionStrings>
  <appSettings file="PrivateSettings.config">
    <add key="owin:AppStartup" value="AspNetIdentity2ExtendingApplicationUser.Startup,AspNetIdentity2ExtendingApplicationUser" />
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="EMAIL_PASSWORD" value="YourEmailPassword"/>
  </appSettings>
</configuration>

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 <add> element(s):

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 ConnectionStrings.config file:

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 ConnectionStringSettings object.

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:
<appSettings file="PrivateSettings.config">
  <add key="owin:AppStartup" value="AspNetIdentity2ExtendingApplicationUser.Startup,AspNetIdentity2ExtendingApplicationUser" />
  <add key="webpages:Version" value="3.0.0.0" />
  <add key="webpages:Enabled" value="false" />
  <add key="ClientValidationEnabled" value="true" />
  <add key="UnobtrusiveJavaScriptEnabled" value="true" />
</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:

Accessing Settings:
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

C#
Setting Up for Mono Development in Linux Mint/Ubuntu
CodeProject
Git: Combine and Organize Messy Commits Using Interactive Rebase
C#
C#: Query Excel and .CSV Files Using LinqToExcel
  • Brad

    BradBrad

    Author Reply

    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.”

    quoted from:
    https://docs.microsoft.com/en-us/dotnet/framework/winforms/advanced/application-settings-overview#limitations-of-application-settings

    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.


  • Andre

    AndreAndre

    Author Reply

    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??


  • jatten

    jattenjatten

    Author Reply

    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.


  • Col

    ColCol

    Author Reply

    If using this approach, does changing a value in an external file trigger an app pool recycle?


  • John

    JohnJohn

    Author Reply

    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.


  • Rick

    RickRick

    Author Reply

    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?


    • Eric

      EricEric

      Author Reply

      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.