Ultimate Guide to the Web.Config File for ASP.Net

The web.config file is an XML file located within the root of the application folder and used to configure the application.

By Tim Trott | C# ASP.Net MVC | January 6, 2009
2,192 words, estimated reading time 8 minutes.

The web.config is used by IIS to configure the application and the server and controls authentication, error pages, debug information, session configuration and much more.

What is the web.config files?

The web.config file is an XML file located within the root of the application folder. It is used to store configuration settings for the current ASP.Net web application. Settings within the web.config are specific to an instance of the application. machine.config should be used for server-wide settings. Settings in the web.config will override settings in the machine.config.

The web.config file can consist of any valid XML tag, the root tag of the document is always 'configuration'. Within this tag can be any number of tags depending on what you are doing.

Some of the main sections are system.web and appSettings which are looked at in more detail below.

System.Web Section of Web.Config

The system.web section is the root element for the configuration that controls how the application behaves.

You can set debug mode on and off in the compilation element.

xml
<compilation debug="false" strict="false" explicit="true">

And you can set the custom asp.net error pages from within the system.web as well:

xml
<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
  <error statusCode="403" redirect="NoAccess.htm" />
  <error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>

When settings mode to RemoteOnly the error page redirects will not be handled when viewing the page on localhost. Other values are On (rules are always processed) and Off (rules are never processed).

You can also secure web applications in this area of the web.config, a topic that is covered in a future tutorial.

Web.Config appSettings Section

The appSettings section is used to provide information that will keep our applications flexible. These settings include connection strings, server names, file paths and any other settings that are dependent on the environment.

Shown below is a minimal appSettings section that holds a connection string.

xml
<appSettings>
  <add key="ConnectionInfo" value="server=(local);database=Northwind;Integrated Security=SSPI" />
<appSettings>

You can then access the data stored in web.config appSettings section from within C# code behind like this:

C#
string connectionString = ConfigurationSettings.AppSettings["ConnectionInfo"];

This technique can be used to eliminate any hard-coded data and allow applications to be more flexible. Any changes can be made without the need for recompilation.

Custom Error Pages

There is a setting in the web.config which you can use to redirect errors for remote users (if you view the page on the localhost web server then you can see the error and debug the code accordingly).

xml
<customErrors defaultRedirect="myErrorPage.aspx" mode="RemoteOnly"/>

If you do not wish errors to be shown at all, not even on localhost, then set the mode to On instead of RemoteOnly.

You can also set custom error pages for specific errors that your code cannot process, for example, a 401 unauthorised, 404 Not Found page or a 500 internal error. Simply add extra lines and change the attributes to meet your needs.

xml
<customErrors mode="On">
  <error statusCode="404" redirect="/errorpages/404.aspx" />
  <error statusCode="500" redirect="/errorpages/500.aspx" />
</customErrors>

Web.config Encryption

It is always a good idea to protect sensitive data such as connection strings so that they are not accessible to anyone without proper authorization. It is highly unlikely that anybody could read these values since they would need direct access to the server, but it is still best practice to encrypt such data. Luckily Microsoft .Net provides a couple of methods for encrypting connection strings, or any other keys, in the web.config. It's also better to store connection strings in the connectionStrings section.

Let's take a look at a simple web.config file.

xml
<configuration>
 <appSettings>
  <add key="myKeyName" value="myValue"/>
  <add key="ProtectMe" value="This is a secret!" />
<appSettings>
  <connectionStrings>
    <add name="myDatabaseConStr" connectionString="Data Source=myServerAddress;Initial Catalog=myDataBase;User Id=myUsername;Password=myPassword;" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <system.web>
    <compilation debug="true"/>
    <authentication mode="Windows"/>
  </system.web>
</configuration>

Method #1 - C#

The first encryption method requires some C# code in the background, a method for encryption and another for decryption. They are very simple and both use the WebConfigurationManager class. This code has been wrapped into a utility class, so all you need to do is copy and paste the code into a new class file.

C#
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Web.Configuration;

// <summary>
// Utility class for encrypting and decrypting the web.config file
// </summary>
public static class WebConfigEncryption
{
  // <summary>
  // Encrypt a section of the web.config
  // </summary>
  // <param name="sectionToEncrypt">Name of the section to encrypt</param>
  // <param name="encryptionProvider">One of the EncryptionProviders to use</param>
  public static void EncryptSection(string sectionToEncrypt)
  {
    Configuration config = WebConfigurationManager.OpenWebConfiguration(HttpContext.Current.Request.ApplicationPath);
    ConfigurationSection section = config.GetSection(sectionToEncrypt);

    if (!section.SectionInformation.IsProtected)
    {
      if (!section.ElementInformation.IsLocked)
      {
        section.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider");
        config.Save(ConfigurationSaveMode.Full);
      }
    }
  }

  // <summary>
  // Decrypt a section of the web.config
  // </summary>
  // <param name="sectionToEncrypt">Name of the section to decrypt</param>
  public static void DecryptSection(string sectionToEncrypt)
  {
    Configuration config = WebConfigurationManager.OpenWebConfiguration(HttpContext.Current.Request.ApplicationPath);
    ConfigurationSection section = config.GetSection(sectionToEncrypt);

    if (section.SectionInformation.IsProtected)
    {
      if (!section.ElementInformation.IsLocked)
      {
        section.SectionInformation.UnprotectSection();
        config.Save(ConfigurationSaveMode.Full);
      }
    }
  }
}

You will notice the use of "RsaProtectedConfigurationProvider" in the encryption code. This specifies the ProtectedConfigurationProvider that .Net will use to encrypt the section.

You can call this code using the following methods to encrypt and decrypt the section.

C#
public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
      / Encrypt appSettings section
      WebConfigEncryption.EncryptSection("appSettings");

      / Decrypt appSettings section
      WebConfigEncryption.DecryptSection("appSettings");
    }
}

You can easily substitute appSettings for connectionStrings to encrypt connection strings as well as or instead of.

Let's have a look at the result from the RsaProtectedConfigurationProvider on the web.config from above.

xml
<configuration>
  <appSettings configProtectionProvider="RsaProtectedConfigurationProvider">
    <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#">
      <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
      <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
        <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
          <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
          <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <KeyName>Rsa Key</KeyName>
          </KeyInfo>
          <CipherData>
           <CipherValue>3YKipPu2e83QASir3owfIs0pmq97VR9dWkPfXouYNArz+Uo2sMxte7Gp303ag6+WMYGqi5sAUuFmJW3KSOnuFyVG0NDneA42eUo6ktG4LVaQoA5DIFJT02PSD5mzkf5VTPvupAuokQLGx9ZNv9qYA+HSXEcn3AKNBvxgBkagKg8=</CipherValue>
          </CipherData>
        </EncryptedKey>
      </KeyInfo>
      <CipherData>
        <CipherValue>X8sdfco1OaOauIbA31Osb+4a+7xeYqiWJa45l1X4ttKvkMM4kZ7Uj6c9yxW2MjwkisIdhPWJHtobjfw8JNe2qnbygBolL3su/iQde5s5HAdvVOcJIOHfeQ==</CipherValue>
      </CipherData>
    </EncryptedData>
  </appSettings>
  <connectionStrings>
    <add name="myDatabaseConStr" connectionString="Data Source=myServerAddress;Initial Catalog=myDataBase;User Id=myUsername;Password=myPassword;" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <system.web>
    <compilation debug="true"/>
    <authentication mode="Windows"/>
  </system.web>
</configuration>

The first thing we notice is that there is a lot of extra information contained in the appSettings section. The first part of the new data contains namespaces that .Net uses to later use and decrypt settings. The two important parts of the new data are the "CipherValue" keys. If you look at the structure of the new appSettings there is a KeyInfo with CipherData and a CipherData out on its own. The KeyInfo contains an encrypted, encryption key. That means that a key (or password) has been generated to encrypt our data. This has then been encrypted with the machine key and stored in the web.config file. The CipherData string on its own contains an encrypted version of our appSettings key/value pair.

Since the encrypted encryption key is encrypted with the machine key (try saying that after a few pints!) it is difficult to distribute the encrypted web.config to a different server. For this, we will need to create custom keys and RSA providers. We will investigate this problem in a future tutorial.

We can also use the decryption code to decrypt the appSettings and return the web.config to its original state.

I have created a simple utility class to encapsulate these functions; the code can be downloaded at the end of this tutorial.

Method #2 - Command Line

The other method for encrypting web.config does not involve any code, instead is based on the command line tool aspnet_regiis.

This command line tool can be found within the %windows%\Microsoft.NET\Framework\versionNumber folder, or can be run directly from the Visual Studio command prompt.

To encrypt a section of the web.config:

C:\>aspnet_regiis -pe "appSettings" -app "/YourAppVirtualDir" -prov "RsaProtectedConfigurationProvider"

And to decrypt it again:

C:\>aspnet_regiis -pd "appSettings" -app "/YourAppVirtualDir"

Regardless of the method used, .Net will automatically decrypt values when you read them back at runtime, so any existing calls to ConfigurationSettings.AppSettings or reading connectionStrings do not need to be modified.

Using encrypted web.config in web farms

As previously mentioned the encryption is based on the machine key stored in machine.config and is unique to each server. Because of this decryption will only work on the server it was encrypted on. There is a way around this problem though by using Key Containers.

The process is fairly simple; you encrypt the web.config on one machine, export the key container and import it on all the other servers.

Let's have a look at the process.

Creating an RSA Key Container

To create an RSA key container we will use the ASP.NET IIS registration tool. We must give the key container a name which will identify the container used by the RsaProtectedConfigurationProvider.

The following command will create an RSA key container named 'SampleKeys' that is a machine-level key container and is exportable.

aspnet_regiis -pc "SampleKeys" "exp

The following example shows the configProtectedData section of a web.config file. The section specifies a RsaProtectedConfigurationProvider that uses a machine-level RSA key container named SampleKeys.

xml
<configProtectedData>
  <providers>
    <add name="SampleProvider" type="System.Configuration.RsaProtectedConfigurationProvider, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a processorArchitecture=MSIL" keyContainerName="SampleKeys" useMachineContainer="true" />
  </providers>
</configProtectedData>

As a security precaution, generated RSA key containers are protected by access control lists (ACLs) on the server. Before we can use the key container we must allow ASP.NET worker process to have read access. In most cases, the worker process uses the NETWORK SERVICE profile. You can check this by viewing the properties of the application pool that the application is running in. The following command will grant the network process access to the key container.

aspnet_regiis -pa "SampleKeys" "NT AUTHORITYNETWORK SERVICE"

Next, we must allow the application's Windows identity access to the machine key container named NetFrameworkConfigurationKey, which is the key container specified for the default provider.

aspnet_regiis -pa "NetFrameworkConfigurationKey" "NT AUTHORITYNETWORK SERVICE"

The NetFrameworkConfigurationKey RSA key container is the default key container for commands issued by the Aspnet_regiis.exe tool. The preceding command could therefore also be issued as the following:

aspnet_regiis -pa "NT AUTHORITYNETWORK SERVICE"

Exporting an RSA Key Container

Now we are over the worst of the process. The next task is to export the key container to an XML document. This is also done using aspnet_regiis.exe. The following command will export our SampleKeys to a file called keys.xml.

aspnet_regiis -px "SampleKeys" keys.xml -pri

For security, after you export an RSA key container to an XML file, copy the XML file to a location external to the server and delete the XML file from the server. This reduces the chance of an attacker gaining access to your RSA key container and thereby the ability to decrypt Web.config files encrypted using that RSA key container.

Importing an RSA Key Container

Almost done. The final step is to import the key container into each of the other servers in the web farm. Copy the keys.xml onto the destination server and import it using the following command:

aspnet_regiis -pi "SampleKeys" keys.xml

Once again, delete the XML file from the server when you're done.

And now we are done!

There is a more in-depth walk through on the Microsoft website Creating and Exporting an RSA Key Container 

Was this article helpful to you?
 

Related ArticlesThese articles may also be of interest to you

CommentsShare your thoughts in the comments below

If you enjoyed reading this article, or it helped you in some way, all I ask in return is you leave a comment below or share this page with your friends. Thank you.

There are no comments yet. Why not get the discussion started?

We respect your privacy, and will not make your email public. Learn how your comment data is processed.