ASP.NET: Securing Config Files in Github - asp.net

In asp.net we can include file attribute in appsettings that can contain secret values:
Web.config
<appSettings file="HiddenSettings.config">
</appSettings>
HiddenSettings.config
<?xml version="1.0"?>
<appSettings>
<add key="DbConnectionString" value="VALUE_IN_TEMPLATE_FILE" />
</appSettings>
In git, we can simply keep the HiddenSettings.config in git-ignore, so that sensitive information will not be checked-in.
The problem with this approach is every time the developer takes the first copy of the code, he/she needs to fill the secret data manually.
What's the best practice to do that automatically (i.e. no manual action to fill secret information in local/target deployment environment)?

I've been pondering that recently myself. The best method I could come up with so far is to link to a separate config file as you did and put that file to a separate private repository. Then add that repository as a submodule to the actual project. For instance:
git submodule add git#github.com:someUser/private-config.git
and the config points to the relative path of the submodule, something like:
<appSettings file="../../../../private-config/The.Real.Deal.config">
<add key="someKey" value="SAMPLE VALUE" />
</appSettings>
So when a user checks out the project (with initializing submodules) the config in the private-repo will be available. If not than they can download the project without the actual values and will only have access to the sample values.
Compared to other methods this looked good to me but I'm always open to suggestions too.

Related

How to get web.config transformation to include external secrets file?

Is there a way to get a web.config transformation in Visual Studio 2015+ to include the contents of an externally-referenced secrets file as described here?
This works well when developing locally, and doesn't include the file in source control, but when I go to deploy the web app to the server, I want it to include the username and password for another web service which it connects to. If I enter the settings manually in IIS on the server, they are lost each time I publish the app.
If I omit the keys in the appSettings block in my web.config (so that they are only referred to in the secrets file), the manually-entered settings in IIS on the server are removed completely whenever I publish the app.
Being able to refer to certain things in an external file for the sake of better security and not checking in passwords to source control isn't turning out to be a very good idea IMHO because my deployment is now a nightmare. I don't want to manually enter the passwords in the web.config file on the server after every deployment.
I'd rather not look into encryption, either, because I would have to do that for each server I deploy to so that the relevant machine key is used for each web.config file.
I've only recently thought about removing this password from source control, in response to a recent push to improve security practises at work - which I well understand and agree with - but I can also see why security is so poorly considered because the life of the ordinary developer becomes extremely unpleasant if there the tools available don't make it easy.
Surely there's a way without resorting to encryption?
Thanks.
Without a CI system I think your best option is probably a pre/post build action that executes a script?
I'd suggest you replace the actual values with tokens for your sensitive web.config values (something unique/easy to find like MY_PRODUCT_DATABASE_PASSWORD etc). Your web.config can then be checked into source control safely.
In VS you can add a build action to run a custom powershell or exe to basically perform a find-and-replace on the tokens with actual values before you zip & deploy as normal.
Exactly how/where you store the real values and how the script works is up to you. you could easily find a file on your deployment machine or a row in some database based on data passed to the script/exe from vs or from data within web.config itself (or embedded as a comment in web.config even).
Here's details of the variables available from vs you could pass to your exe or script in a build action: https://msdn.microsoft.com/en-us/library/42x5kfw4.aspx
It you wanted to use PowerShell you could read/replace/write values to a web.config(or any text file) like this answer: How can I replace every occurrence of a String in a file with PowerShell?
Following the Microsoft Docs example you've linked to, putting this XDT transformation inside Web.Release.config should do the trick:
<appSettings file="..\..\AppSettingsSecrets.config" xdt:Transform="SetAttributes">
<add key="mailAccount" xdt:Locator="Match(key)" xdt:Transform="Remove" />
<add key="mailPassword" xdt:Locator="Match(key)" xdt:Transform="Remove" />
<add key="TwilioSid" xdt:Locator="Match(key)" xdt:Transform="Remove" />
<add key="TwilioToken" xdt:Locator="Match(key)" xdt:Transform="Remove" />
<add key="TwilioFromPhone" xdt:Locator="Match(key)" xdt:Transform="Remove" />
<add key="GoogClientID" xdt:Locator="Match(key)" xdt:Transform="Remove" />
<add key="GoogClientSecret" xdt:Locator="Match(key)" xdt:Transform="Remove" />
</appSettings>
You dont need xdt:Locator attribute on <appSettings> element itself, because there is only one appSettings.
Edit: I've misunderstood the original question. The goal is to include contents of referenced file, which is not possible using XDT. There has to be another way.

Can included sections in Web.config be encrypted

I have an ASP.NET MVC5 website in development which I will shortly need to deploy to an IIS8 webserver. I'm trying to get the security model for the web.config file right, and in particular I want to:
Prevent secrets in the web.config file being exposed in my source control system
Protect the deployed web.config from prying eyes (I don't own the server).
From searching on SO and other sites I can see that there are specific tools/techniques to address each scenario:
'Included' sections in web.config that do not get saved to the SCCS.
Encrypted web.config files. Or encrypted sections of the file to be more precise.
I'm fine with both of those, but I can not for the life of me see how to combine the two techniques to solve both problems simultaneously. Is it possible to encrypt an external section? Is this even the right approach given that many of the answers are several years old now and address older versions of ASP.NET/MVC.
I can't be the first do want to do this so I'm sure I'm missing something obvious.
It has been suggested that this might already be answered here, however that question is about encrypting sections in the main web.config file, and I am asking about encrypting external sections. By that I mean sections that are 'included' using the configSource XML attribute.
It's probably bad form to answer ones own question, but I had a flash of inspiration and after a couple of hours of experimentation I have it working how I want.
The bit I had got all wrong was that I was trying to encrypt the external files. It does not work like that. Here's how it does work, at least, this is how it works for me on an IIS8.5 and ASP.NET v4.0.30319 server.
ConnectionStrings
Create the connectionStrings section in a separate file, e.g. Web.connectionStrings.config:
<?xml version="1.0"?>
<connectionStrings>
<add name="MyConnection" connectionString="{your connection string here}"
providerName="System.Data.SqlClient" />
</connectionStrings>
Ref this file from web.config:
<connectionStrings configSource="Web.connectionStrings.config" />
Make sure the external file is not under source code control so it does not get uploaded to your SCCS.
Deploy BOTH files, either as part of your deployment process or deploy the secure file manually if you're really paranoid.
Encrypt the connectionStrings section of the web.config normally, using the aspnet_regiis.exe command mentioned in the article mentioned by Afzaal. This process actually encrypts the contents of the Web.connectionStrings.config file and leaves the web.config file unchanged. You need to leave the external file in place but as it is now encrypted this is quite safe.
appSettings
Create your security-critical settings in a separate file, e.g. Web.appSettings.config.
<?xml version="1.0"?>
<appSettings>
<add key="wc1" value="web.app.config1" />
<add key="wc2" value="web.app.config2" />
</appSettings>
Ref this file from web.config:
<appSettings file="Web.App.config">
{other non-secure appSettings}
</appSettings>
Again, ensure the secure file is not under source control, and deploy both files to the production server.
Encrypt the appSettings section of the web.config file.
Unlike the connectionStrings section, this does not alter the external file at all. Instead, settings from both web.config and the external file are merged (external file takes precedence if duplicate keys are encountered) and are stored in an encrypted form in web.config.
At this point you can remove the Web.appSettings.config file as its contents are now incorporated into the main file.
Points to note:
If you introduce another Web.appSettings.config file at a later time, and restart the site, the contents of that file will override the encrypted settings in web.config. This may or may not be useful. When you remove the file and restart the site, the settings revert to the encrypted ones again.
If you decrypt the appSettings section, ALL the current settings are written back into the main web.config file, including those that originally came from the external file. You'll need to remember to remove them if you're just changing a setting and then re-encrypting the file again.

Web.config appSettings configSource attribute transformation

I am trying to update the configSource attribute on the appSettings element within my web.config file using web.config transformations.
I have the following in my web.config
<appSettings configSource="Config\appSettings.local.config">
</appSettings>
I want this to be
<appSettings configSource="Config\appSettings.prod.config">
</appSettings>
when i build Release. This is not happening. I have the following in my Web.Release.config in the element
<appSettings xdt:Transform="SetAttributes(configSource)" configSource="Config\appSettings.prod.config" />
Transformations only happen when you deploy the web application (or create a deployment package). It does not happen when you simply build the solution.
http://msdn.microsoft.com/en-us/library/dd465326.aspx
"For Web application projects, ASP.NET provides tools that automate the process of changing (transforming) Web.config files when they are deployed."
You can cause the transformation to happen on build if you like though, with a little poking around in your project file. I wrote a post on this a while back, its centered on app.config but it will work for web.config you can just miss a few steps out!
http://www.chrissurfleet.co.uk/post/2011/07/27/Faking-Webconfig-transformations-in-appConfig.aspx

msdeploy sync and web.config

I am using msdeploy to transfer my changes(via a nant script in Team City) that I make to a site and it is great!! I have just one question, I am using msdeploy with the sync feature to make my life easier.
I currently exclude the web.config in my msdeploy because I do not know how to change the web.config on the fly. How do I change the web.config on the destination site if I do a sync?
Suppose you have a source directory with a web.config file that looks like this:
<configuration>
<system.web>
<randomSection name="value" name2="value2" />
</system.web>
</configuration>
And you want to change the "name" attribute to "GoGermany"
msdeploy -verb:sync -source:dirpath=c:\source -dest:dirpath=c:\dest
-setParam:type=XmlFile,match=//randomSection/#name,scope=web.config$,value=GoGermany
This will sync the two directories, while converting web.configs to change the #name attribute. The "match" subparameter is an X-Path selecting the attribute to change.
You can also do the parameter using type="TextFile" in which case you can do a regex match/replace against the entire file. The example above uses XmlFile which means an XML attribute transform.
Hope that helps.

How to create generic web.config file for different web servers in .net web application?

I want to create a generic web.config file for different web servers in VB.NET. So, depending on the server configuration requirements, applications can retrieve all values from that generic configuration file.
Is this possible? How would I do this?
This is just a random idea, it may not fit your needs though. You could create a configuration section for each server named by the name of the server. Create a helper class for reading configuration values that checks for any values in the section named after the server's name first, if it doesn't exist read it from a default configuration section.
I'm still not sure if this would be a wise decision, its just an option.
Technically, there's the machine.config which includes settings that apply to the entire machine.
web.config files can override some settings from it.
For everything that stays the same, use the a single web.config.
For everything that changes, use a reference to an external file.
<configuration>
<appSettings file="ExternalWeb.config">
<add key="MyKey" value="MyValue" />
</appSettings>
...
</configuration>
http://www.devx.com/vb2themax/Tip/18880
This way when things change in the main web.config, few things must be updated.
You may also consider using templates and code generation techniques to generate a web.config for each server.
How about a "mode" appsetting key/value. This "mode" can be set to "dev", "testing", "prod", etc. Then, set the mode of the current configuration file and prefix all the settings that would change with the mode.
Example:
<add key="mode" value="test" /> <!-- possible values: dev, test, prod -->
<add key="dev.dbconnstr" value="data source=DB;userid=ABC;password=DEF" />
<add key="test.dbconnstr" value="data source=DB;userid=ABC;password=DEF" />
<add key="prod.dbconnstr" value="data source=DB;userid=###;password=###" />
Then, use a configuration class to read the setting depending on the mode.
Example:
mode = ConfigurationManager.AppSettings("mode");
CongifurationManager.AppSettings(mode + ".dbconnstr");
Doing it this way, you can have the same config file deployed to all servers, and never have to worry about tweaking each server (except of course updating the "mode" value when deploying). I would also recommend not saving the production credentials in the other configuration files, instead replace it with a placeholder.
You could create a deployment script in something like nant which loads in a web.config containing placeholders for the configuration options. This could then replace the placeholders for the appropriate environments.

Resources