The Configuration API in .NET 2.0

Monday, July 4, 2005

The configuration API in .NET 2.0 gives us the ability to read and update configuration files, including web.config and machine.config files. You can read and write configuration files for your application, for another application on the same machine, or even an application on a different server. In this article, we will take a look at some of the highlights of the configuration API from the perspective of an ASP.NET developer, including how to use encryption and alternate configuration files.

AppSettings and Connection Strings

Two common tasks in ASP.NET development are reading application setting strings and connection strings from the configuration file. In .NET 2.0 these settings reside in the <appSettings> and <connectionStrings> respectively. A sample web.config file for an ASP.NET site might look like the following.

<?xml version="1.0"?>

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

 

  <appSettings>

    <add key="message" value="Hello World!" />

  </appSettings>

 

  <connectionStrings>

    <add name="AdventureWorks" connectionString="..."/>

    <add name="pubs" connectionString="..."/>

  </connectionStrings>   

 

    <system.web>

    <compilation debug="true" />

    <authentication mode="Windows"/>

    <identity impersonate="true"/>

  </system.web>

 

</configuration>

The configuration API for ASP.NET developers begins with the WebConfigurationManager class in the System.Web.Configuration namespace. The WebConfigurationManager includes static (shared) properties to fetch application settings and connection string. For example, to read the “message” appSetting from the web.config we could use the following code:

string message;

message = WebConfigurationManager.AppSettings["message"];

Similarly, if we want to grab the second connection string, the connection with the name of “pubs”, we could use the following code:

string connectionString =

    WebConfigurationManager.ConnectionStrings["pubs"].ConnectionString;

The configuration API makes easy work of reading any setting in a configuration file using the GetSection static method. GetSection takes an XPath expression to indicate the section you want to get, and you can coerce the resulting object reference into a strongly typed reference for built-in section types. For instance, there is an AuthorizationSection class to manipulate the settings inside the <authorization> section, and a PagesSection class to manipulate the settings in the <pages> section.

If we want to write out the value of the impersonate attribute in the <identity> section of web.config, we could use the following:

protected void readImpersonationButton_Click(object sender, EventArgs e)

{

    // note: currently broken in BETA2, works in post BETA2 builds.

    // in BETA2 GetSection returns a wrapper

    // that will not cast to IdentitySection

    IdentitySection section;

    section = WebConfigurationManager.GetSection("system.web/identity")

                    as IdentitySection;

 

    if (section != null)

    {

        WriteMessage("Impersonate = " + section.Impersonate);

    }

}

 

private void WriteMessage(string message)

{

    // this method assumes a PlaceHolder control

    // on the web form with the ID of messagePlaceHolder

    HtmlGenericControl generic = new HtmlGenericControl();

    generic.InnerHtml = message;

    messagePlaceHolder.Controls.Add(generic);

}

Modify Configuration Files

The WebConfigurationManager class also allows us to open a web configuration for update using the static method OpenWebConfiguration. We can open a configuration file inside of our application by passing just a relative path. We can also read configuration files in other applications by passing the IIS sitename and a virtual directory. It’s even possible to open application configuration files on another machine.

If we want to toggle the debug attribute in the <compilation>section of the web.config for the current application from true to false and back again, we could use the following code in the event handler for a button click event:

protected void toggleDebugButton_Click(object sender, EventArgs e)

{

    Configuration config;

    config = WebConfigurationManager.OpenWebConfiguration("~");

 

    CompilationSection compilation;

    compilation = config.GetSection("system.web/compilation")

                      as CompilationSection;

 

    if (compilation != null)

    {

        compilation.Debug = !compilation.Debug;

        config.Save();

        WriteMessage(

                "Debug setting is now: " + compilation.Debug

            );

    }

}

Using a strongly typed CompilationSection object allows to use to read and write the attributes inside a <compilation> section. We can make changes to this section (and any others) and save all the changes at once using the Save method of the System.Configuration.Configuration object returned from OpenWebConfiguration.

There are a few caveats to updating configuration files. First, your application will need write permissions on the file. Typically, the NETWORK SERVICE and ASPNET accounts used by the runtime do not have write permissions on files and directories in an application’s home folder. One safe way to approach the problem is the technique used here - a combination of Windows authentication and impersonation. These settings allow the request to execute with the identity of the client. If the client has write permissions to the configuration file, the above snippet will be successful.

Another caveat is that the ASP.NET runtime watches web.config and will restart the web application when a change occurs. Specifically, the runtime will create a new instance of your application inside of a new AppDomain anytime you write to web.config. A restart can have a performance impact, so writing to web.config should not occur often.

If you need more control over permissions and application restarts when it comes to updating web.config, you might want to look at using external configuration sources, as described in the next section.

Using an External Configuration Source

You can take any configuration section and place the section into it’s own, dedicated file.. As an example, let’s take a look at a new version of our web.config file:

<?xml version="1.0"?>

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

 

  <appSettings configSource="appSettings.config"/>

 

  <connectionStrings configSource="connections.config"/>   

 

  <system.web>

    <compilation debug="true" />

    <authentication mode="Windows"/>

    <identity impersonate="true"/>

  </system.web>

</configuration>

In this example, we’ve moved our <appSettings> and <connectionStrings> sections into external files. These external files will be XML fragments containing a single section element, for instance, the appSettings.config file will look like the following.

<appSettings>

  <add key="message" value="Hello World!"/>

</appSettings>

Using an external configuration source can be useful in a number of scenarios. For instance, you could place a section into an external configSource if you needed an easy method to swap settings for the section depending on the environment (development, test, or production).

You could also use an external configSource if you needed granular control over permissions. For instance, you could lock down your web.config file so that only Administrators could modify the file, but keep the <appSettings> section in an external file that additional roles could modify

There is an additional benefit to using an external file, and that is the ability to have some amount of control over application restarts. If the web.config files changes, the application will restart – there is no alternative. However, if you move a section into an external file, you can tell the runtime if it should, or should not restart the application when the external configuration source changes.

If you look inside of the machine.config file for your computer, in the <configSections> area, you’ll see where a section handler type is defined for each configuration section. Each <section> entry can include an attribute: restartOnExternalChanges. Notice the <section> configuration for the appSettings section uses restartOnExternalChanges="false". This means if your appSettings section lives in an external file, and changes are made to the file, the application will not restart, but you will see the new values in calls to WebConfigurationManager.AppSettings.

Use restartOnExternalChanges with some care, as some parameters can truly only take effect if the application restarts. If you do set restartOnExternalChanges to false for a section, make sure not to cache the parameters for the section in our application, and always read values through the WebConfigurationManager.

Using Encryption

Encrypting an entire section of a configuration file is straightforward with the 2.0 configuration API. There are several configuration areas where sensitive information may appear, for instance, the <connectionStrings> section may contain database usernames and passwords, the <identity> section will contain a username and password when you need the runtime to impersonate a fixed identity. You may even keep a password for a third party web service in appSettings or a custom section. Whenever secrets like these appear, consider encrypting the section instead of leaving the secrets and passwords in plain text.

Note: there are sections that may contain passwords that you cannot encrypt, namely the <processModel> section. You can still use the Aspnet_setreg.exe tool to store a password for this section securely.

The following section of code shows how easy it is to protect (encrypt) and unprotect (decrypt) an entire configuration section. (Note: you do not need to unprotect a section in order to read configuration settings from the section. The runtime will read the encrypted data and perform the decryption necessary for your application to read the plain text values. The Unprotect method call is here to demonstrate how to return a section to unencrypted form).

protected void toggleEncryptionButton_Click(object sender, EventArgs e)

{

    Configuration config;

   config = WebConfigurationManager.OpenWebConfiguration("~");

 

    ConnectionStringsSection section;

    section = config.GetSection("connectionStrings")

                    as ConnectionStringsSection;

 

    if (section.SectionInformation.IsProtected)

    {

        section.SectionInformation.UnprotectSection();

    }

    else

    {

        section.SectionInformation.ProtectSection(

                "DataProtectionConfigurationProvider"

            );

    }

 

    config.Save();

    WriteMessage("connections protected = " +

                    section.SectionInformation.IsProtected);

}

If we were to examine our web.config file after toggling encryption to on, we’d notice the configuration API has added some additional information:

<?xml version="1.0"?>

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

 

  <protectedData>

    <protectedDataSections>

      <add name="connectionStrings"

           provider="DataProtectionConfigurationProvider"

           inheritedByChildren="false" />

    </protectedDataSections>

  </protectedData>

  <appSettings configSource="appSettings.config"/>

 

  <connectionStrings configSource="connections.config"/>   

 

  <system.web>

    <compilation debug="true" />

    <authentication mode="Windows"/>

    <identity impersonate="true"/>

  </system.web>

</configuration>

In addition, we’d find our connectionStrings.config file would contain a cipherValue instead of plaintext connection strings. (Note: we do not need to use an external configuration source to take advantage of encryption, the configuration API would have happily encrypted the connection strings section if it lived inside of web.config).

<connectionStrings>

<EncryptedData>

  <CipherData>

   <CipherValue>AQAAANCMnd8BF....</CipherValue>

  </CipherData>

</EncryptedData>

</connectionStrings>

At runtime, the configuration API will decrypt sections on the fly. We can still use WebConfigurationManager.ConnectionStrings to return connection strings usable by our application.

To understand what we are seeing in the configuration file, we first need to realize that the runtime turns to a configuration encryption provider for encryption and decryption work. The two providers shipping in .NET 2.0 are the DataProtectionConfigurationProvider and the RSAProtectedConfigurationProvider (you can also implement your own protected configuration provider if need be). We can specify the provider we want to use in the string passed to the ProtectSection method, as seen in the earlier source code snippet. In our example we are using the DataProtectionConfigurationProvider.

The DataProtectionConfigurationProvider uses the Windows Data Protection API (DPAPI) underneath the covers. This provider a machine-specific secret key for encryption and decryption work. Because the DataProtectionConfigurationProvider relies on a machine-specific key, you can only decrypt cipher text that was encrypted on the same machine.

If you need to move configuration files with encrypted sections from machine to machine, you’ll need the RSAProtectedConfigurationProvider. The RSAProtectedConfigurationProvider, as the name would imply, uses RSA public key encryption. You can work with the RSAProtectedConfigurationProvider from the command line tool aspnet_regiis, which includes options to create a keypair (-pc), export a keypair (-px), import a keypair (-pi), grant access to a keypair (-pa), remove access (-pr), and more. Command line arguments also allow you to specify which encryption provider to use.

In Summary

The configuration API and associated command line tools offer flexibility and convenience in updating, encrypting, and managing configuration files. When modifying configuration files, think about application restart issues and how to ensure write permissions to the configuration files.

by K. Scott Allen

 

Feel free to leave comments or questions about this article on my blog.

by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!