ASP.NET
uses a hierarchical system of configuration. At the top of the
hierarchy is the Machine.config file. This file contains all the default
configuration settings for ASP.NET applications and all other types of
applications built with the .NET Framework.
The Machine.config file is located at the following path:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\Machine.config
This same
folder also contains a Web.config file. The Web.config file contains
settings specific to ASP.NET applications. The Web.config file overrides
particular settings in the Machine.config file.
The \CONFIG folder includes the following six files:
Machine.config—Contains the actual configuration settings.
Machine.config.default—Contains the default values for all configuration settings.
Machine.config.comments—Contains comments on each configuration setting.
Web.config—Contains the actual configuration settings.
Web.config.default—Contains the default values for all configuration settings.
Web.config.comments—Contains comments on each configuration setting.
Only the
Machine.config and Web.config files are actually used. The other files
are there for the purpose of documentation. You can place a Web.config
file in the root folder of a website, such as the wwwroot
folder.
A Web.config file located in the root folder of a website contains
settings that apply to all applications contained in the website. You
also can place a Web.config file in the root of a particular
application. In that case, the Web.config file has application scope.
Finally, you can place a Web.config file in an application subfolder. In
that case, the Web.config file applies to all pages in that folder and
below.
When an ASP.NET
application starts, this hierarchy of configuration files is merged and
cached in memory. A file dependency is created between the cached
configuration settings and the file system. If you make a change to any
of the configuration files in the hierarchy, the new configuration
settings are loaded into memory automatically. When an ASP.NET page
reads a configuration setting, the setting is read from memory. This
means that the ASP.NET Framework can read configuration settings, such
as connection strings, very efficiently.
Modifying
most configuration settings results in an application restart. Any data
stored using the cache or in-process Session state is lost and must be
reloaded. You can get around this issue by using external configuration
files. See the section “Placing Configuration Settings in an External
File” later in this chapter. The configuration files are XML files. You
can modify configuration settings by opening the Machine.config file or a
Web.config file and modifying a setting in Notepad. Alternatively, you
can change many of the configuration settings (but not all) by using
either the Web Site Administration Tool or the ASP.NET Microsoft
Management Console Snap-In. Using the Web Site Administration Tool If
you are using Visual Web Developer (or Visual Studio .NET), then you can
modify certain configuration settings with the Web Site Administration
Tool. This tool provides you with a form interface for making
configuration changes. You open the Web Site Administration Tool by
selecting the menu option Website, ASP.NET Configuration. Selecting this
option opens a browser window that contains the tool.
The Web Site Administration Tool has the following four tabs:
Home—This tab contains links to the other tabs.
Security—This tab enables you to configure authentication, authorization, and the Role Manager.
Application—This
tab enables you to create and manage application settings, configure
SMTP settings, and enable application tracing, debugging, and error
pages.
You also can use this tab to take your application offline.
Provider—This tab enables you to select a provider for Membership and the Role Manager.
Under the
Application tab, you can click the link to take your application
offline. When you click this link, the following httpRuntime element is
added to your web configuration file:
<httpRuntime enable=”false” />
This
setting causes the Application Domain associated with the ASP.NET
application to refuse any requests. When an application is offline, all
requests result in a 404—Not Found error message. You might want to take
your application offline, for example, to prevent people from
requesting pages while you perform updates to your application.
You also
can take an ASP.NET application offline by adding a file with the name
app_offline.htm to the root of your application. The Web Site
Administration Tool is implemented as an ASP.NET application. Behind the
scenes, it uses the Configuration API that is discussed later in this
chapter. You can view the entire source code for the Web Site
Administration Tool by navigating to the following folder:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ASP.NETWebAdminFiles
Using the ASP.NET Microsoft Management Console Snap-In
You
also can make configuration changes with the ASP.NET Microsoft
Management Console (MMC) Snap-In tool (see Figure 28.2). You can open
the ASP.NET MMC Snap-In by following these steps:
1. Open Internet Information Services from Start, Control Panel, Administrative Tools.
2. Open the property sheet for either a website or a virtual directory.
3. Select the ASP.NET tab.
4. Click the Edit Configuration (or Edit Global Configuration) button.
The ASP.NET MMC Snap-In includes the following tabs:
General—Enables you to configure connection strings and application settings.
Custom Errors—Enables you to configure custom error pages.
Overview of Website Configuration
Authorization—Enables you to configure authorization rules.
Authentication—Enables you to configure Forms, Windows, or Passport authentication.
Application—Enables you to configure application settings such as application-wide Master Pages and Themes.
State Management—Enables you to configure Session state.
Locations—Enables you to apply configuration settings to a particular folder or page.
Behind the scenes, the ASP.NET MMC Snap-In uses the Configuration API to make changes to web configuration files.
ASP.NET Configuration Sections
All
the configuration sections in the Machine.config or Web.config file
related to ASP.NET are contained in the <system.web> section
group. Here is a complete list of the 36 ASP.NET configuration sections
and a brief explanation of the purpose of each section:
anonymousIdentification—Enables you to configure anonymous user identification, which is used, for example, by the Profile object.
authentication—Enables you to configure authentication.
authorization—Enables you to configure authorization.
browserCaps—Enables you to configure the lookup of browser capabilities.
caching—Enables you to configure caching.
clientTarget—Enables you to configure aliases for different clients (browsers).
compilation—Enables
you to configure how ASP.NET applications are compiled.For example, you
can specify whether an application is compiled in debug mode.
customErrors—Enables you to configure custom error pages.
deployment—Enables you to specify whether an ASP.NET application is deployed in retail mode.
deviceFilters—Enables you to configure device filters.
globalization—Enables you to configure the Culture, UICulture, and other attributes related to building multi-lingual web applications.
healthMonitoring—Enables you to configure Health Monitoring.
hostingEnvironment—Enables you to configure ASP.NET application properties such as the application idle timeout.
httpCookies—Enables you to configure how cookies are sent to the browser.
httpHandlers—Enables you to configure HTTP Handlers.
httpRuntime—Enables you to configure properties of the HTTP Runtime, such as the number of threads maintained in the thread pool.
httpModules—Enables you to configure HTTP Modules.
identity—Enables you to configure the identity of the ASP.NET application account.
machineKey—Enables you to configure encryption keys used by Membership and Session state.
membership—Enables you to configure ASP.NET Membership.
mobileControls—Enables you to configure adapters used with ASP.NET mobile controls.
Overview of Website Configuration
pages—Enables you to configure page properties such as the website Master Page and Theme
processModel—Enables you to configure the ASP.NET process.
profile—Enables you to configure the Profile object.
roleManager—Enables you to configure the Role Manager.
securityPolicy—Enables you to map security policy files to trust levels.
sessionPageState—Enables you to configure how mobile devices store Session state.
sessionState—Enables you to configure Session state.
siteMap—Enables you to configure Site Maps.
trace—Enables you to configure page and application tracing.
trust—Enables you to configure Code Access Security (CAS) for an ASP.NET application.
urlMappings—Enables you to remap page requests to new pages.
webControls—Enables you to specify the location of client-script files used by web controls.
webParts—Enables you to configure Web Parts.
webServices—Enables you to configure web services.
xhtmlConformance—Enables you to configure the level of XHTML conformance of the XHTML rendered by web controls.
Applying Configuration Settings to a Particular Path
By
default, the settings in a Machine.config or Web.config file are
applied to all pages in the same folder and below. However, if you have
the need, you can also apply configuration settings to a particular
path. For example, you can apply configuration settings to a particular
subfolder or even a particular page. You apply configuration settings to
a particular path by using the <location> element. For example,
the web configuration file in Listing enables password-protection for a
single file named Secret.aspx.
LISTING Web.config
<configuration >
<system.web>
<authentication mode=”Forms” />
</system.web>
<location path=”Secret.aspx”>
<system.web>
<authorization>
<deny users=”?” />
</authorization>
</system.web>
</location>
</configuration>
If you
attempt to request the Secret.aspx page, you are redirected to the
Login.aspx page. However, none of the other files in the same
application are password protected by the configuration file. The
<location> element must be added as an immediate child of the
<configuration> element. You can’t, for example, add the
<location> element within a <system.web> element. You must
surround the <system.web> element with the <location>
element. You can create the web configuration file in Listing by
selecting the menu option Website, Add New Item, and selecting the Web
Configuration File template.
Alternatively,
you can add the appSettings section by using either the Web Site
Administration Tool or the ASP.NET MMC Snap-In. Both tools enable you to
enter values for the appSettings section through a user-friendly
interface.
Locking Configuration Settings
You
can lock configuration settings so that they cannot be overridden at a
lower level in the configuration hierarchy. For example, you might want
to require that no application running on your production server execute
in debug mode. In that case, you can lock the debug configuration
setting in a website Web.config file, the root Web.config file, or the
Machine.config file. You can lock a configuration setting in multiple
ways. The Web.config file in Listing illustrates how you can lock a
setting by using the allowOverride=”false” attribute of the
<location> element.
LISTING Web.config
<configuration >
<location allowOverride=”false”>
<system.web>
<compilation debug=”false” />
</system.web>
</location>
</configuration>
As an
alternative to locking the compilation section to prevent a production
website being deployed in debug mode, you can take advantage of the
deployment element. Adding the following element to the system.web
section of the machine.config disables debug mode, enables remote custom
errors, and disables trace:
<deployment retail=”true” />
One problem
with the configuration file in Listing is that it locks the entire
compilation element. If you attempt to change any attribute of the
compilation element at a lower level in the configuration hierarchy,
then an exception is raised. You can add any of the following attributes
to a particular configuration element to lock either the entire element
or one or more of its attributes:
lockAllAttributesExcept—Enables
you to lock all attributes except those listed as the value of this
attribute. You can specify multiple attributes to exclude in a
comma-delimited list.
lockAllElementsExcept—Enables
you to lock all child elements of the current element except those
listed as the value of this attribute. You can specify multiple elements
to exclude in a comma-delimited list.
lockAttributes—Enables you to lock multiple attributes. You can specify the attributes to lock in a comma-delimited list.
lockElements—Enables you to lock multiple child elements. You can specify the child elements to lock in a comma-delimited list.
lockItem—Enables you to lock the current element.
For
example, the web configuration file in Listing locks the debug
attribute, and only the debug attribute, of the <compilation>
element.
LISTING Web.config
<configuration >
<system.web>
<compilation debug=”false” lockAttributes=”debug” />
</system.web>
</configuration>
Adding Custom Application Settings
You
can add custom configuration settings to the web configuration file
easily by taking advantage of the appSettings section. The appSettings
section contains a list of key and value pairs. For example, the web
configuration file in Listing contains a welcome message and a copyright
notice.
LISTING Web.config
<configuration>
<appSettings>
<add key=”welcome” value=”Welcome to our Web site!” />
<add key=”copyright” value=”Copyright (c) 2007 by the company” />
</appSettings>
</configuration>
You can
retrieve values from the appSettings section either programmatically or
declaratively. The page in Listing illustrates both approaches.
LISTING ShowAppSettings.aspx
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Web.Configuration” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<script runat=”server”>
void Page_Load()
{
lblWelcome.Text = WebConfigurationManager.AppSettings[“welcome”];
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
FIGURE 28.4 Displaying values from the appSettings configuration section.
1454 CHAPTER 28 Configuring Applications
LISTING 28.5 Continued
<head id=”Head1” runat=”server”>
<title>Show AppSettings</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:Label id=”lblWelcome” Runat=”server” />
<hr />
<asp:Literal id=”ltlCopyright” Text=”<%$ AppSettings:copyright %>” Runat=”server” />
</div>
</form>
</body>
</html>
Placing Configuration Settings in an External File
You
can place particular configuration sections in an external file. You
might want to do this for a couple of reasons. First, you can make a
configuration file more manageable by dividing it into multiple files.
Also, when you place configuration information in a separate file, you
can prevent application restarts when you change a configuration
setting. Every configuration element includes a configSource attribute.
You can assign a path to a file as the value of the configSource
attribute. For example, the web configuration file in Listing 28.6 uses
the configSource attribute in its <appSettings> element.
LISTING Web.config
<configuration>
<appSettings configSource=”appSettings.config” />
</configuration>
<appSettings>
<add key=”message” value=”Hello World!” />
</appSettings>
Using the Configuration API
The
Configuration API enables you to retrieve and modify configuration
settings. You can use the Configuration API to modify web configuration
files on the local machine or a remote machine. If you are responsible
for maintaining a large number of websites, the Configuration API can
make your life much easier. You can build administrative tools that
enable you to make configuration changes quickly to multiple
applications. You can use the Configuration API in an ASP.NET page, or
you can build command-line tools or Windows Forms applications that use
the Configuration API.
The
Configuration API is exposed by the WebConfigurationManager class
(located in the System.Web.Configuration namespace). This class supports
the following properties:
AppSettings—Exposes all the settings from the appSettings section.
ConnectionStrings—Exposes all the settings from the connectionStrings section.
The WebConfigurationManager also supports the following methods:
GetSection—Retrieves a configuration section relative to the current page or a supplied virtual path.
GetWebApplicationSection—Retrieves a configuration section from the current web application root web configuration file.
OpenMachineConfiguration—Retrieves a Machine.config file on either the local machine or a remote server.
OpenMappedMachineConfiguration—Retrieves a Machine.config file by using a particular file mapping.
OpenMappedWebConfiguration—Retrieves a web configuration file by using a particular file mapping.
OpenWebConfiguration—Retrieves a Web.config file on either the local machine or a remote server.
Almost
every configuration section in the web configuration file has a
corresponding class in the .NET Framework that represents the
configuration section. These classes provide you with a strongly typed
representation of each configuration section. For example, corresponding
to the <authentication> section in the web configuration file,
there is a System.Web.Configuration.AuthenticationSection class.
Corresponding to the <pages> section in the web configuration
file, there is a System.Web. Configuration.PagesSection class. Each of
these classes expose properties that correspond to all the attributes
you can set in the web configuration file.
Reading Configuration Sections from the Current Application
When
an ASP.NET application starts, the application merges all the
configuration settings in the configuration hierarchy to create one
representation of the configuration settings. A particular configuration
setting might have different values at different levels in the
hierarchy. You can use the methods of the WebConfigurationManager class
to get the value of a configuration setting at any level in the
hierarchy.
The
WebConfigurationManager.GetWebApplicationSection() method always
retrieves a configuration setting from the application root Web.config
file. For example, the page in Listing 28.8 displays whether debugging
is enabled.
LISTING ShowConfigApp.aspx
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Web.Configuration” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<script runat=”server”>
void Page_Load()
{
CompilationSection section = (CompilationSection)WebConfigurationManager.
➥GetWebApplicationSection(“system.web/compilation”);
lblDebug.Text = section.Debug.ToString();
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show Config App</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
Debug Mode:
<asp:Label id=”lblDebug” Runat=”server” />
</div>
</form>
</body>
</html>
The
GetWebApplication() method returns an object. You must cast the value
returned by this method to a particular configuration section type. In
Listing, the value returned by this method is cast to an instance of the
CompilationSection type. Realize that you will get the same result when
the page in Listing is located in different subfolders. For example,
debugging might not be enabled in a root configuration file, but it
might be enabled in a configuration file in a particular subfolder.
However, if you call the GetWebApplicationSection() method, the method
always returns the configuration setting for the application root
Web.config file.
If you want
to get the value of a configuration setting relative to the folder in
which the page executes, then you can use the GetSection() method
instead of the GetWebApplicationSection() method. The page in Listing is
located in a subfolder. The page displays the value of the debug
setting retrieved from both the GetWebApplicationSection() method and
the GetSection() method.
LISTING SubFolder\ShowConfigRelative.aspx
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Web.Configuration” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<script runat=”server”>
void Page_Load()
{
CompilationSection section =
(CompilationSection)WebConfigurationManager.GetSection(“system.web/compilation”);
lblDebug1.Text = section.Debug.ToString();
section = (CompilationSection)WebConfigurationManager.
➥GetWebApplicationSection(“system.web/compilation”);
lblDebug2.Text = section.Debug.ToString();
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show Config Relative</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
GetSection Debug:
<asp:Label id=”lblDebug1” Runat=”server” />
<br /><br />
GetWebApplicationSection Debug:
<asp:Label id=”lblDebug2” Runat=”server” />
</div>
</form>
</body>
</html>
Before you
can use the classes from the System.DirectoryServices namespace, you
must add a reference to the System.DirectoryServices.dll assembly. In
Visual Web Developer, select the menu option Website, Add Reference.
Opening a Configuration File on a Remote Server
You
can use the WebConfigurationManager class to open Machine.config or
Web.config files located on remote web servers. However, before you can
do this, you must perform one configuration step. You must enable the
remote server to accept remote configuration connections by executing
the following command from a command prompt:
aspnet_regiis -config+
To disable remove configuration connections, execute the following command: aspnet_regiis -config-
The aspnet_regiis tool is located in the following path:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis.exe
If you open
the SDK Command Prompt, then you don’t need to navigate to the
Microsoft.NET folder to execute the aspnet_regiis tool. After you make
this modification to a remote server, you can retrieve (and modify)
configuration settings on the remote server by using one of the Open
methods exposed by the WebConfigurationManager class. For example, the
page in Listing contains a form that enables you to enter a server,
username, and password. When you submit the form, the page connects to
the remote server and retrieves its Machine.config file. The page
displays the current value of the remote server’s authentication mode).
LISTING ShowConfigRemote.aspx
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Web.Configuration” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<script runat=”server”>
protected void btnSubmit_Click(object sender, EventArgs e)
{
try
{
Configuration config = WebConfigurationManager.
➥OpenMachineConfiguration(null, txtServer.Text, txtUserName.Text,
➥txtPassword.Text);
AuthenticationSection section = (AuthenticationSection)config.
➥GetSection(“system.web/authentication”);
lblAuthenticationMode.Text = section.Mode.ToString();
}
catch (Exception ex)
{
lblAuthenticationMode.Text = ex.Message;
}
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show Config Remote</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:Label id=”lblServer” Text=”Server:” AssociatedControlID=”txtServer” Runat=”server” />
<br />
<asp:TextBox id=”txtServer” Runat=”server” />
<br /><br />
<asp:Label id=”lblUserName” Text=”User Name:” AssociatedControlID=”txtUserName”
Runat=”server” />
<br />
<asp:TextBox id=”txtUserName” Runat=”server” />
<br /><br />
<asp:Label id=”lblPassword” Text=”Password:” AssociatedControlID=”txtPassword”
Runat=”server” />
<br />
<asp:TextBox id=”txtPassword” TextMode=”Password” Runat=”server” />
<br /><br />
<asp:Button id=”btnSubmit” Text=”Submit” OnClick=”btnSubmit_Click” Runat=”server” />
<hr />
Authentication Mode:
<asp:Label id=”lblAuthenticationMode” Runat=”server” />
</div>
</form>
</body>
</html>
Modifying Configuration Sections
You
can use the WebConfigurationManager class not only when opening a
configuration file to read the values of various configuration settings
but to modify existing configuration settings or add new ones.
The Configuration class supports two methods for saving configuration information: the Save() and SaveAs() methods.
Creating a Configuration Element Collection
A
configuration element can contain a collection of child elements. For
example, if you need to create a custom configuration section to
configure a provider, then you use child elements to represent the list
of providers. The class in Listing represents a configuration section
for a ShoppingCart. The configuration section includes three properties:
MaximumItems, DefaultProvider, and Providers. The Providers property
represents a collection of shopping cart providers.
LISTING App_Code\ShoppingCartSection.cs
using System;
using System.Configuration;
namespace AspNetUnleashed
{
public class ShoppingCartSection : ConfigurationSection
{
[ConfigurationProperty(“maximumItems”, DefaultValue = 100, IsRequired =
➥true)]
public int MaximumItems
{
get { return (int)this[“maximumItems”]; }
set { this[“maximumItems”] = value; }
}
[ConfigurationProperty(“defaultProvider”)]
public string DefaultProvider
{
get { return (string)this[“defaultProvider”]; }
set { this[“defaultProvider”] = value; }
}
[ConfigurationProperty(“providers”, IsDefaultCollection = false)]
public ProviderSettingsCollection Providers
{
get { return (ProviderSettingsCollection)this[“providers”]; }
}
public ShoppingCartSection(int maximumItems, string defaultProvider)
{
this.MaximumItems = maximumItems;
this.DefaultProvider = defaultProvider;
}
1484 CHAPTER 28 Configuring Applications
LISTING 28.21 Continued
public ShoppingCartSection()
{
}
}
}
The
Providers property returns an instance of the ProviderSettingsCollection
class. This class is contained in the System.Configuration namespace.
The web configuration file in Listing illustrates how you can use the
ShoppingCartSection.
LISTING Web.config
<configuration>
<configSections>
<sectionGroup name=”system.web”>
<section name=”shoppingCart” type=”AspNetUnleashed.ShoppingCartSection” allowLocation=”true”
allowDefinition=”Everywhere” />
</sectionGroup>
</configSections>
<system.web>
<shoppingCart maximumItems=”50” defaultProvider=”SqlShoppingCartProvider”>
<providers>
<add name=”SqlShoppingCartProvider” type=”AspNetUnleashed.SqlShoppingCartProvider” />
<add name=”XmlShoppingCartProvider” type=”AspNetUnleashed.XmlShoppingCartProvider” />
</providers>
</shoppingCart>
</system.web>
</configuration>
LISTING App_Code\AdminUsersSection.cs
using System;
using System.Configuration;
namespace AspNetUnleashed
{
public class AdminUsersSection : ConfigurationSection
{
[ConfigurationProperty(““, IsDefaultCollection = true)]
public AdminUsersCollection Users
{
get { return (AdminUsersCollection)this[““]; }
}
public AdminUsersSection()
{
}
}
public class AdminUsersCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new AdminUser();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((AdminUser)element).Name;
}
public AdminUsersCollection()
{
this.AddElementName = “user”;
}
}
public class AdminUser : ConfigurationElement
{
[ConfigurationProperty(“name”, IsRequired = true, IsKey = true)]
public string Name
{
get { return (string)this[“name”]; }
set { this[“name”] = value; }
}
[ConfigurationProperty(“password”, IsRequired = true)]
public string Password
{
get { return (string)this[“password”]; }
set { this[“password”] = value; }
}
}
}
Notice that
the ConfigurationProperty attribute that decorates the Users property
sets the name of the configuration attribute to an empty string. It also
marks the property as representing the section’s default collection.
These options enable you to avoid having to create a subtag for the user
collection. The user collection appears immediately below the main
<adminUsers> section tag.
The ASP.NET page in Listing displays all the users from the adminUsers section in a BulletedList control
LISTING ShowAdminUsersSection.aspx
<%@ Page Language=”C#” %>
<%@ Import Namespace=”AspNetUnleashed” %>
<%@ Import Namespace=”System.Web.Configuration” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<script runat=”server”>
void Page_Load()
{
// Get configuration
AdminUsersSection section = (AdminUsersSection)WebConfigurationManager.
➥GetWebApplicationSection(“system.web/adminUsers”);
// Bind section to GridView
bltAdminUsers.DataSource = section.Users;
bltAdminUsers.DataBind();
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show AdminUsersSection</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<h1>Administrators</h1>
<asp:BulletedList
id=”bltAdminUsers”
DataTextField=”Name”
Runat=”server” />
</div>
</form>
</body>
</html>
Creating Encrypted Configuration Sections
If
you need to protect sensitive information stored in a configuration
file, you can encrypt the information. For example, you should always
encrypt the connectionStrings section of a configuration file to prevent
your database connection strings from being stolen by evil hackers. You
can encrypt just about any section in the web configuration file. You
can encrypt any of the sections in the system.web section group with the
sole exception of the processModel section. You also can encrypt a
custom configuration section.
The .NET
Framework uses the Provider model for encrypting configuration sections.
The Framework ships with two ProtectedConfigurationProviders: the
RsaProtectedConfigurationProvider and the
DpapiProtectedConfigurationProvider. The
RsaProtectedConfigurationProvider protect sensitive information stored
in a configuration file, you can encrypt is the default provider. It
uses the RSA algorithm to protect a configuration section. The RSA
algorithm uses public key cryptography. It depends on the fact that no
one has discovered an efficient method to factor large prime numbers.
The second
provider, the DpapiProtectedConfigurationProvider, uses the Data
Protection API (DPAPI) to encrypt a configuration section. The DPAPI is
built into the Windows operating system (Microsoft Windows 2000 and
later). It uses either Triple-DES or AES (the United States
Government–standard encryption algorithm) to encrypt data. The
RsaProtectedConfigurationProvider is the default provider, and it is the
one that you should almost always use. The advantage of the
RsaProtectedConfigurationProvider is that this provider supports
exporting and importing encryption keys. This means that you can move an
application that contains an encrypted configuration file from one web
server a new web server. For example, you can encrypt a configuration
section on your development web server and deploy the application to a
production server. If you use the DpapiProtectedConfigurationProvider to
encrypt a configuration section, on the other hand, then you cannot
decrypt the configuration section on another web server. If you need to
move the configuration file from one server to another, then you need to
first decrypt the configuration file on the source server and
re-encrypt the configuration file on the destination server.
The .NET
Framework uses the World Wide Web Consortium (W3C) recommendation for
encrypting XML files. This recommendation is located at www.w3.org/TR/2002/REC-xmlenc-core-20021210. You can use encryption not only with configuration files, but also with other XML files.
To learn more about encrypting XML files, look up the EncryptedXml class in the Microsoft .NET Framework SDK Documentation.
Encrypting Sections with the aspnet_regiis tool
The
easiest way to encrypt a section in the web configuration file is to
use the aspnet_regiis command-line tool. This tool is located at the
following path:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis.exe
You don’t
need to navigate to the Microsoft.NET directory to execute the
aspnet_regiis tool if you open the SDK Command Prompt. If you want to
encrypt a particular section of a configuration file, then you can use
the -pef option when executing the aspnet_regiis tool. For example, the
following command encrypts the connectionStrings section of a
configuration file located in a folder named MyWebApp:
aspnet_regiis -pef connectionStrings c:\Websites\MyWebApp
If you
prefer, rather than specify the location of a web application by its
file system path, you can use its virtual path. The following command
encrypts the connectionStrings section of a configuration file located
in a virtual directory named /MyApp:
aspnet_regiis -pe connectionStrings -app /MyApp
Notice that
the -app option is used to specify the application’s virtual path. You
can decrypt a configuration section by using the -pdf option. The
following command decrypts a configuration file located in a folder
named MyWebApp:
aspnet_regiis -pdf connectionStrings c:\Websites\MyWebApp
You also
can decrypt a configuration section by specifying a virtual directory.
The following command uses the -pd option with the -app option:
aspnet_regiis -pd connectionStrings -app /MyApp
If you
execute the following command, then the connectionStrings section is
encrypted with the DataProtectionConfigurationProvider:
aspnet_regiis -pe connectionStrings -app /MyApp –prov ➥ProtectedConfigurationProvider
Notice that this command includes a -prov option that enables you to specify the ProtectedConfigurationProvider.
Encrypting Sections Programmatically
Instead
of using the aspnet_regiis tool to encrypt configuration sections, you
can use the Configuration API. Specifically, you can encrypt a
configuration section by calling the SectionInformation.ProtectSection()
method. For example, the ASP.NET page in Listing displays all the
sections contained in the system.web section group in a GridView
control. You can click Protect to encrypt a section, and you can click
UnProtect to decrypt a section (see Figure 28.14).
LISTING EncryptConfig.aspx
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Web.Configuration” %>
<%@ Import Namespace=”System.Collections.Generic” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<script runat=”server”>
void Page_Load()
{
if (!Page.IsPostBack)
BindSections();
}
protected void grdSections_RowCommand(object sender, GridViewCommandEventArgs e)
{
int rowIndex = Int32.Parse((string)e.CommandArgument);
string sectionName = (string)grdSections.DataKeys[rowIndex].Value;
if (e.CommandName == “Protect”)
ProtectSection(sectionName);
if (e.CommandName == “UnProtect”)
UnProtectSection(sectionName);
BindSections();
}
private void ProtectSection(string sectionName)
{
Configuration config = WebConfigurationManager.OpenWebConfiguration
➥(Request.ApplicationPath);
ConfigurationSection section = config.GetSection(sectionName);
section.SectionInformation.ProtectSection
➥(“RsaProtectedConfigurationProvider”);
config.Save(ConfigurationSaveMode.Modified);
}
private void UnProtectSection(string sectionName)
{
Configuration config = WebConfigurationManager.OpenWebConfiguration
➥(Request.ApplicationPath);
ConfigurationSection section = config.GetSection(sectionName);
section.SectionInformation.UnprotectSection();
config.Save(ConfigurationSaveMode.Modified);
}
private void BindSections()
{
Configuration config = WebConfigurationManager.OpenWebConfiguration
➥(Request.ApplicationPath);
List<SectionInformation> colSections = new List<SectionInformation>();
foreach (ConfigurationSection section in
➥config.SectionGroups[“system.web”].Sections)
colSections.Add(section.SectionInformation);
grdSections.DataSource = colSections;
grdSections.DataBind();
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Encrypt Config</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:GridView id=”grdSections” DataKeyNames=”SectionName” AutoGenerateColumns=”false”
OnRowCommand=”grdSections_RowCommand” Runat=”server” >
<Columns>
<asp:ButtonField ButtonType=”Link” Text=”Protect” CommandName=”Protect” />
<asp:ButtonField ButtonType=”Link” Text=”UnProtect” CommandName=
➥”UnProtect” />
<asp:CheckBoxField DataField=”IsProtected” HeaderText=”Protected” />
<asp:BoundField DataField=”SectionName” HeaderText=”Section” />
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html>
When you
click the Protect link, the grdSection_RowCommand() event handler
executes and calls the ProtectSection() method. This method calls the
SectionInformation. ProtectSection() method to encrypt the selected
section. Notice that the name of a ProtectedConfigurationProvider is
passed to the ProtectSection() method.
The page in
Listing saves the configuration file. By default, the ASPNET and
NETWORK SERVICE accounts do not have permission to write to the file
system. If you want the page in Listing 28.26 to execute within the
security context of the user requesting the page, then you can enable
per-request impersonation by adding the configuration file in Listing to
the root of your application.
Deploying Encrypted Web Configuration Files
If you need to copy an encrypted configuration file from one server to a new server, then
you must copy the keys used to encrypt the configuration file to the new server. Otherwise,
your application can’t read encrypted sections of the configuration file on the new server.
You can’t
copy an encrypted configuration file from one server to another when you
are using the DpapiProtectedConfigurationProvider. This section assumes
that you are using the RsaProtectedConfigurationProvider. By default,
the RsaProtectedConfigurationProvider uses a public/private key pair
stored in a key container named NetFrameworkConfigurationKey. This key
container is located at the following path:
\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys
If you want
to deploy an application that contains an encrypted configuration file
to a new server, then you must configure a new key container and import
the key container to the new server. You must complete five
configuration steps:
1. Create a new key container.
2. Configure your application to use the new key container.
3. Export the keys from the origin server.
4. Import the keys on the destination server.
5. Grant access to the key container to your ASP.NET application.
You need to
perform this sequence of configuration steps only once. After you have
set up both servers to use the same encryption keys, you can copy
ASP.NET applications back and forth between the two servers and read the
encrypted configuration sections. Let’s examine each of these steps one
by one.
First, you
need to create a new key container because the default key container,
the NetFrameworkConfigurationKey key container, does not support
exporting both the public and private encryption keys. Execute the
following command from a command prompt:
aspnet_regiis -pc “SharedKeys” –exp
This
command creates a new key container named SharedKeys. The -exp option is
used to make any keys added to the container exportable. After you
create the new key container, you must configure your application to use
it. The web configuration file in Listing configures
RsaProtectedConfigurationProvider to use the SharedKeys key container.
LISTING Web.config
<configuration>
<configProtectedData
defaultProvider=”MyProtectedConfigurationProvider”>
<providers>
<add name=”MyProtectedConfigurationProvider”
type=”System.Configuration.RsaProtectedConfigurationProvider” cspProviderName=””
useMachineContainer=”true” useOAEP=”false” keyContainerName=”SharedKeys” />
</providers>
</configProtectedData>
<connectionStrings>
<add name=”Movies” connectionString=”Data Source=DataServer;Integrated Security=true;
Initial Catalog=MyDB” />
</connectionStrings>
</configuration>
Notice that
the configuration file in Listing includes a configProtectedData
section. This section is used to configure a new
ProtectedConfigurationProvider named MyProtectedConfigurationProvider.
This provider includes a keyContainerName attribute that points to the
SharedKeys key container. The next step is to export the keys contained
in the SharedKeys key container to an XML file. You can export the
contents of the SharedKeys key container by executing the following
command:
aspnet_regiis -px “SharedKeys” keys.xml –pri
The final
step is to grant access to the key container to your ASP.NET
application. By default, a page served from Internet Information Server
executes within the security context of either the NT Authority\NETWORK
SERVICE account (Windows 2003 Server or Vista) or the ASPNET account
(other operating systems). You can grant access to the SharedKeys key
container to the ASPNET account by executing the following command:
aspnet_regiis -pa “SharedKeys” “ASPNET”
Executing
this command modifies the ACLs for the SharedKeys key container so that
the ASPNET account has access to the encryption keys.After you complete
this final step, you can transfer ASP.NET applications with encrypted
configuration files back and forth between the two servers. An
application on one server can read configuration files that were
encrypted on the other server.As an alternative to using the
aspnet_regiis tool, you can transfer encryption keys with the help of
the RsaProtectedConfigurationProvider class. The
RsaProtectedConfigurationProvider class contains methods for exporting
and importing keys to and from XML files programmatically.