So, how do you manage connection strings in your ASP.NET applications? Since version 2.0 of ASP.NET, the most common answer to this was: By placing them all in the <connectionStrings> section of the root <configuration> node of the web.config.
The next logical question would be: How do you make sure that wrong people can't read those strings? By encrypting them, of course. After all, doesn't Microsoft itself enforce this approach? Didn't all well-established publications make this a de-facto method of connection string management in ASP.NET?
Yes, they did. But let's examine all this one more time, this time from a slightly different angle.
Indeed, we do need a reliable way to protect our connections from preying eyes. And encryption seems to be the only choice here. But let's list the concerns that programmers typically have when they manage encrypted connections in ASP.NET these days:
- The mother of all questions here must be this one: Where to store the encryption keys? As soon as you ask that main question, many sub-questions begin to raise. Who should have access to those keys? What if one or more of those individuals leave the company? Who is enforcing the logistics of key handling? What is the recovery procedure if any of those keys was lost or compromised? Who would enforce that procedure? How does he know that he needs to enforce that procedure right now? It's good if your company has a solid "security forcing" process already in place. But what if it doesn't?
- You are almost guaranteed to have issues with encrypted connections if your code is running on a web farm, in a cloud or is load-balanced.
- You need to code something extra just to make your connections accessible to the rest of your app.
- Normally, we need one connection per environment, at least for development and production. It looks really weird when you see all those connections in the same section of the web.config, all having different names like "DevConn_Server_GHYT65TY" and "StagingConn_Local". To combat this, some companies sell "solutions" that manage differences in your configuration files. I have seen one such solution in action at one of our clients. To use it, you must create multiple web.config files in the same web app project, one for each environment. You are supposed to give those files some random unique extensions like web.config.prod or web.config.dev and "register" those files with their server. The software is "smart" enough to rename those files to web.config when it publishes (simply FTPing) the project to the server. What really sucks is that the cost of this "system" is not 10 or 20 bucks. It's several thousand. Amazing... UPDATE: It breaks my heart to see that MS takes the same direction in ASP.NET 4.0 and MVC with those Web.Debug.config and Web.Release.config instead of referencing configuration sections declared in separate files. But please keep reading...
- Here is my "favorite" concern: here at CodeEffects.com we frequently need to access many different databases, local and remote ones. I can't just remember dozens of client server locations, database names, logins, passwords and connection specifics. And I don't want to run a project in debug mode every time I need to see the connection to its database in clear text. So, if we would encrypt all our connections, our shop would need some kind of repository where we would keep them in a readable format. And that would pretty much make the entire encryption effort useless for obvious reasons.
Now that we listed our concerns, let's examine what we really want to achieve here:
- We want to restrict certain groups of people from accessing certain resources. For example, developers must be able to connect to development databases but they must not be able to access the production environment or even see the production credentials.
- If a person has access to certain resources, he or she should be able to quickly access details of any connection without applying any extra effort.
- Our connections can be specific not only to the application itself, but also to the server that currently runs it.
- We should have only one connection per database in our configuration files, not all those "DevConn" or "ProdServerConn". And it would be great if names of those connections stay the same no matter which server we are currently on.
- We want to be able to set this once for each environment and truly forget about it, even when deploying the entire application, unless some changes have been made to the connection itself.
Here is what I don't understand: you can achieve all that I just mentioned above in seconds right now without degrading the security of your application in any way and you were able to do this since the very first public beta of ASP.NET (April of 2000, I believe). And yet, almost no one brings this to your attention.
Let's do this: create a test web application. Make sure there is a web.config file. Add a new XML file to the root folder of the project and name it AppSettings.config. Add the following content to that new file:
<?xml version="1.0" encoding="utf-8" ?>
<appSettings>
<add key="DbConnection" value="-- whatever your connection string is --"/>
</appSettings>
Note that you can omit the XML declaration on top of the file - ASP.NET parser will understand it properly anyway.
Now, add the "file" attribute to the <appSettings> node of your web.config file with the value of the file name of that AppSettings.config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings file="AppSettings.config">
<add key="SomeTestKey" value="SomeTestValue"/>
</appSettings>
</configuration>
The little dirty secret here is that the "file" attribute tells the parser to load the AppSettings.config file while it reads the web.config. It then "injects" its content into the <appSettings> node. Now you can access the value of that "DbConnection" key from the AppSetttings.config file as you would normally access any other "appSettings" key from the web.config:
using System.Configuration;
namespace Test.Web.Application
{
internal class Settings
{
public static string DbConnection
{
get { return ConfigurationManager.AppSettings["DbConnection"]; }
}
}
}
(I assume you're a good professional and rather than accessing configuration values from your pages or forms directly, you have a type that exposes those values as its strongly typed properties.)
I think, it's clear by now that this approach gives us a much better and simpler management of our connections. We can create multiple setting files, one for every server or environment. Those files can contain values specific to their current environments. You create them once, save them on their servers and truly forget about environment-specific settings. Every time you deploy an application to a different environment, you move all application files except for the settings file.
This allows us not to encrypt connections at all. And yet we can reliably control who has access to those connections. Therefore, our first three concerns were just eliminated all at once.
Most developers shouldn't have access to a production environment anyway. With separate setting files in place, developers will never see the production or other "important" connections because they are not the ones who created those files. Programmers can move between projects, leave the company - rest assured that they simply cannot take company's sensitive data with them. So, we eliminated our last concern as well. And we achieved everything that we wanted to achieve.
But wait a second..! Or, in Russian: Се-е-екундочку..! :) No one said that everything above applies to DB connections only.
For example,
payment gateway credentials for shopping carts. Does the current environment use "sandboxes" or "live" values. What about
SMTP credentials? Or
server-specific paths? And so on, and so forth. Basically, you can (and should) keep all static environment-independent settings in the web.config itself and move all others to the setting file.
Take a look at the
MSDN documentation of <appSettings> node. In the "file" description, notice the phrase "In the .NET Framework version 2.0, you can now include configuration settings in a separate file for all configuration elements that support the configSource attribute". The possibilities are truly endless!