Managing Web Server Modules in Internet Information Services (IIS) 7.0
- 3/5/2008
- Extensibility in IIS 7.0
- Runtime Web Server Extensibility
- Summary
- Additional Resources
Runtime Web Server Extensibility
The Web server extensibility model provides a foundation for IIS 7.0’s modular architecture, enabling the low-footprint, low attack surface area, as well as highly specialized Web server deployments. All of this is possible because built-in IIS 7.0 features are implemented as pluggable modules on top of the same extensibility APIs that are exposed to third-party modules and are configured and managed with the same configuration and management tools.
What Is a Module?
Logically, a module is a Web server component that takes part in the processing of some or all requests and typically provides a service that can involve anything from supporting specific authentication methods (such as the Windows Authentication module) to recording and reporting requests that are currently executing (such as the Request Monitor module). The modules operate by executing during different stages of the request processing pipeline and influencing the request processing by using the APIs exposed by the Web server extensibility model. The majority of modules provide independent services to add functionality to the Web application or otherwise enhance the Web server.
The application developer or IT administrator can then essentially put together the Web server with the precise functionality that is required by controlling which modules are enabled for the application, much like building a structure from a set of LEGO blocks. IIS 7.0 provides a fine degree of control over which modules are enabled, giving administrators control of functionality on both the server as a whole and of specific applications, which we’ll cover in depth later in this chapter.
Physically, IIS 7.0 modules are implemented as either native dynamic-link libraries (DLLs) developed on top of the new IIS 7.0 native C++ extensibility model, or as managed .NET Framework classes that leverage the new ASP.NET integration model available in IIS 7.0. Both of these APIs enable modules to participate in the IIS 7.0 request processing pipeline, and manipulate the request and response processing as needed. Though these two extensibility models use two different APIs and have a number of different characteristics from both the developer and IT administrator perspective, they both implement the logical module concept. This enables IIS 7.0 to provide a consistent development abstraction to both C++ and .NET Framework developers for extending the Web server. The logical module concept also enables IIS 7.0 to expose the administrator to a largely unified view of managing the Web server feature set. For information on the differences between native and managed Web server modules and how they affect the installation and management of modules, see the section titled “Differences Between Managed (.NET) and Native (C++) Modules“ later in this chapter.
The Request Processing Pipeline
The IIS 7.0 request processing pipeline is the foundation of the modular architecture, enabling multiple independent modules to provide valuable services for the same request.
The pipeline itself is essentially a deterministic state machine that enables modules to interact with the request during a fixed set of processing stages, also known as events. As shown in Figure 12-2, when the request is received, the state machine proceeds from the initial stage toward the final stage, raising the events and giving each module an opportunity to do its work during the stages it is interested in.
Figure 12-2 The request processing pipeline.
The majority of events in the request processing pipeline are intended for a specific type of task, such as authentication, authorization, caching, or logging. Modules that subscribe to these events can provide a specific service appropriate for the particular stage. For example, the authenticate event is home to a number of IIS 7.0 modules, including the Windows Authentication module (NTLM and Kerberos authentication), the Basic Authentication module, the ASP.NET Forms Authentication module, and so on. These events enable multiple modules to execute during the request processing and perform typical Web server processing tasks in the correct order. For example, determining the user associated with the request during the authentication stage needs to happen before determining whether that user has access to the requested resource during the authorization stage.
Other events are present for additional flexibility, enabling modules to perform tasks at a specific time during request processing (typically between the events that have specific intended roles such as authentication and authorization). Table 12-1 lists all the events, along with some IIS 7.0 modules that subscribe to them.
Table 12-1 Request Processing Events
Event |
Description |
Modules |
BeginRequest |
The request processing is starting. |
Request Filtering module, IP Restrictions module |
AuthenticateRequest |
The authenticated user for the request is determined. |
Authentication modules, including Windows Authentication module, Basic Authentication module, ASP.NET Forms Authentication module |
AuthorizeRequest |
Access to the requested resource is checked for the authenticated user, and the request is rejected if access is denied. |
URL Authorization module |
ResolveRequestCache |
The server checks if the response to this request can be retrieved from a cache. |
IIS Output Cache module, ASP.NET Output Cache module |
MapRequestHandler |
The handler for this request is determined. |
|
AcquireRequestState |
The required state for this request is retrieved. |
ASP.NET Session State module |
PreExecuteRequestHandler |
The server is about to execute the handler. |
|
ExecuteRequestHandler |
The handler for the request executes and produces the response. |
All modules that provide request handling, including Static File module, Directory Listing module, Default Document module, ISAPI extension module, ASP.NET PageHandler |
ReleaseRequestState |
The state is released. |
ASP.NET Session State module |
UpdateRequestCache |
The cache is updated. |
IIS 7.0 Output Cache module, ASP.NET Output Cache module |
LogRequest |
The request is logged. |
Custom Logging module |
EndRequest[*] |
The request processing is about to finish. |
Request Monitor module |
It is important to understand that though the majority of modules are self-contained and provide independent services during request processing, they do operate on a common set of request and response state and can affect the other’s operation. In some cases, these relationships are part of formal patterns (such as the authentication and authorization pattern), and in others they may be unintentional. In the latter case, some modules may not be compatible with each other, or they may require a specific ordering to function correctly. Module ordering is discussed in the section titled “Controlling Module Ordering” later in this chapter.
Differences Between Managed (.NET) and Native (C++) Modules
As we mentioned earlier, IIS 7.0 supports modules developed with the native IIS 7.0 C++ API as well as modules developed using the ASP.NET module API, sometimes referred to as managed modules.
The IIS 7.0 C++ module API replaces the legacy ISAPI filter and extension API as the new native extensibility model for IIS 7.0 and future versions of IIS. Existing ISAPI filters and extensions are still supported, but developers are encouraged to take advantage of the module extensibility model to build new server components. In fact, the support for ISAPI filters and extensions in IIS 7.0 is implemented as a native module, developed with the new native API, which hosts and executes ISAPI DLLs. Modules developed using the new native API are similar to the ISAPI filters in that they are Win32 DLLs loaded in-process by each IIS worker process, and they can affect the processing of every request. Because they execute under the rights and privileges of the IIS worker process, they have the same security impact and therefore have to be trusted by the server administrator.
However, this is where many of the similarities end, because IIS 7.0 modules use a much more refined and significantly more powerful C++ API, have access to many more extensibility points by subscribing to one or more of the events in the request processing pipeline, and can accomplish much richer tasks. The new C++ API also significantly improves the server development experience and reduces the potential for reliability issues that plagued the overly complex ISAPI interface. This makes IIS 7.0 native modules the most powerful—and yet simpler and more reliable—way to extend IIS.
Also, for the first time in the history of IIS, IIS 7.0 provides a full-fidelity .NET extensibility model based on ASP.NET. This makes server development significantly more accessible to developers and enables them to rapidly build server features while taking advantage of powerful features of ASP.NET and the .NET Framework. This is made possible by the new ASP.NET integration engine, which elevates ASP.NET from being an application framework to being a first-class extensibility mechanism for IIS 7.0.
As a server administrator, extending IIS with the .NET Framework enables you to delegate IIS extensibility to application owners who do not have administrator privileges on the server. This is possible because of the Code Access Security (CAS)–based ASP.NET hosting model, which constrains the execution of code in ASP.NET applications when configured to run with partial trust. Unlike native modules that execute with full privileges of the IIS worker process, managed ASP.NET modules can execute with limited privileges that can prevent them from negatively affecting the server itself or other applications on the server. This enables IIS 7.0 applications to deploy IIS features to the server without requiring administrative action (such as installing COM objects or ISAPI filters), without compromising server security.
Table 12-2 is a summary of the differences between native and managed modules.
Table 12-2 Comparing Native and Managed Modules
|
Native Modules |
Managed Modules |
Developed with |
IIS 7.0 C++ module API |
ASP.NET module API, any .NET language |
Represented by |
Win32 DLL |
.NET class in a .NET assembly DLL |
Scope of execution |
IIS worker process |
ASP.NET application domain |
Execution privilege |
IIS worker process identity |
IIS Worker process identity, plus constrained by the ASP.NET Trust Level |
Deployment model |
Globally for the entire server |
Globally for the entire server by using the .NET Global Assembly Cache, or xcopy-deploy inside a specific application |
Deployment privilege |
Administrators only |
Application owners can deploy with application |
IIS 7.0 configuration is aware of the differences between native and managed modules. It also enables administrators to take full advantage of the constrained execution nature of managed modules by enabling managed modules to be added on a per-application basis by packaging them together with the application content. Application-based deployment of managed modules allows for simple xcopy deployment of IIS applications because they can specify their own IIS configuration and modules.
However, IIS 7.0 also provides a consistent view of managing modules, whether they are native or managed, so that administrators can control the server feature set in a standard manner regardless of the module type. You will review the differences in the installation of native and managed modules, as well as standard management tasks, later in this section.
Installing Modules
The modules that comprise the IIS 7.0 feature set in Windows Vista or Windows Server 2008 can be installed via Windows Setup. Thanks to the modular architecture, Windows Setup enables very fine-grained installation of IIS 7.0 features—you can install most of the IIS 7.0 modules separately (along with all of their supporting configuration and administration features). You can also install the .NET Extensibility role service, which enables ASP.NET managed modules to run on IIS 7.0, or the ASP.NET role service, which also installs all of the of the ASP.NET managed modules and handlers to support fully functional ASP.NET applications. You can learn more about installing IIS 7.0 features in Chapter 5.
Windows Setup actually uses the same IIS 7.0 configuration APIs that you can use to manually install a third-party module on the server. In fact, Windows Setup uses Appcmd.exe, the IIS 7.0 command line tool, to perform module installation, which is just one of the ways that you can install modules on IIS 7.0. Later in this chapter, you will look at the most common ways to perform the installation, which are IIS Manager and Appcmd.exe, as well as editing server configuration directly. Of course you also have the option of using any of the programmatic APIs, including the .NET Microsoft.Web.Administration API, the IIS 7.0 configuration COM objects from C++ programs or script, or WMI. The choice is yours.
Installing Native Modules
To install a native module, it must be registered with the system.webServer/globalModules configuration section at the server level, in the ApplicationHost.config configuration file. Because only server administrators have access to this file, the installation of native modules requires Administrative privileges on the server. This is by design—allowing native code to execute in the IIS worker process is a potential security risk, and so Administrators must be sure to trust the source of the module.
The globalModules section contains an entry for each native module installed on the server, specifying the module name and the module image, which is the physical path to the module DLL.
<globalModules> <add name="UriCacheModule" image="%windir%\System32\inetsrv\cachuri.dll" /> <add name="FileCacheModule" image="%windir%\System32\inetsrv\cachfile.dll" /> <add name="TokenCacheModule" image="%windir%\System32\inetsrv\cachtokn.dll" /> <add name="HttpCacheModule" image="%windir%\System32\inetsrv\cachhttp.dll" /> <add name="StaticCompressionModule" image="%windir%\System32\inetsrv\compstat.dll" /> <add name="DefaultDocumentModule" image="%windir%\System32\inetsrv\defdoc.dll" /> ... </globalModules>
The image attribute is an expanded string, which means that it can contain environment variables (as it does for modules installed by Windows Setup). This is a good practice to make sure that the ApplicationHost.config file remains portable and can be copied between servers and works on servers with different system drives.
The act of registering a native module instructs IIS worker processes in all application pools to load the module DLL. The globalModules configuration section is also one of the few sections that cause IIS worker processes to recycle whenever changes are made. This means that you can install new modules, or uninstall existing modules, and IIS will automatically pick up those changes without needing to manually recycle application pools, restart IIS services, or run IISRESET.
After the module is installed, it will be loaded by all IIS worker processes on the server. Unfortunately, IIS 7.0 does not enable native modules to be installed for a particular application pool, so there is no easy way to load a native module only into certain application pools and not into others.
However, loading the module alone is not sufficient to enable the module to execute. It also needs to be enabled by listing its name in the system.webServer/modules configuration section. This is an important distinction that serves to provide more flexible control over the enabled module set. Unlike the globalModules section, which can only be specified at the server level, the modules configuration section can be specified at the application level, such as in the application’s root Web.config. This enables each application to control the set of enabled modules that process requests to itself.
Typically, a native module is also enabled at the server level (in ApplicationHost.config) during its installation, which enables it for all applications on the server by default (except for applications that specifically remove it in their configuration). This is the case for most of the built-in native modules.
<modules> <add name="HttpCacheModule" /> <add name="StaticCompressionModule" /> <add name="DefaultDocumentModule" /> ... <modules>
Each native module is enabled simply by listing its name in the modules collection.
Uninstalling Native Modules
To uninstall a native module, you need to remove the corresponding module entry from the globalModules list. This prevents the module from being loaded in IIS worker processes on the entire server. Removing the module from globalModules causes all IIS worker processes to gracefully recycle.
In addition, when the native module is removed from the globalModules list, references to it in the modules list also must be removed. Otherwise, all requests to the server or an application that enables the missing module will generate an “HTTP 500 – Internal Server Error” error until the module entry is removed. Typically, you should remove both the globalModules and modules entry for the module at the same time. However, if you do it in two separate steps, changing the modules section will not cause a worker process recycle—IIS will automatically pick up this change by recycling any affected applications. Be sure to make a configuration backup in case you need to restore the original configuration later.
When uninstalling a native module that is part of the IIS 7.0 feature set, you should instead uninstall the corresponding IIS Windows Setup component (Windows Vista) or Role Service (Windows Server 2008). Doing so has the benefit of removing the module binaries and related configuration components, as well as indicating that the corresponding feature is not installed to the Windows Setup infrastructure. The binaries remain stored in the OS installation cache, where they are inaccessible to anyone other than the OS TrustedInstaller subsystem. This ensures that you can re-install these modules later, and that any required patches are applied to these binaries even when the patches are not installed on your server.
When a custom module is uninstalled and all IIS worker processes have recycled, you can remove the module binary from the machine if necessary.
Look in the sections titled “Using IIS Manager to Install and Manage Modules” and “Using Appcmd to Install and Manage Modules” later in this chapter to find steps detailing how you can use IIS Manager or the Appcmd command line tool to uninstall a native module.
Installing Managed Modules
Managed modules developed using the ASP.NET APIs are not required to be installed globally on the server. Instead, they simply need to be enabled in configuration for the application where they are to be used, similar to classic ASP.NET applications in previous versions of IIS. This enables simple xcopy deployment of applications containing managed modules, since unlike native modules they do not require Administrative privileges to be deployed.
Needless to say, this makes managed modules very appealing in scenarios in which the application administrator does not have administrative privileges on the server, such as on shared hosting servers or departmental servers. Such applications can now deploy Web server features without contacting the server administrator to install a global and trusted component, which is often not possible. In these environments, the server administrator can constrain the execution of managed modules by limiting the trust of the ASP.NET applications. The section titled “Securing Web Server Modules” later in this chapter covers constraining the execution of managed modules in more detail and discusses locking down module extensibility.
To install a managed module, the module simply needs to be added to the modules configuration section. This is the same section that enables installed native modules, except managed modules do not have to be listed in the globalModules configuration section. The modules section, therefore, provides a unified view of enabled modules, whether they are native or managed. Because this configuration section can be delegated down to the application level, each application can specify the complete set of enabled modules (managed or native) by using its modules configuration. Here is a more complete example of the modules configuration section at the server level after the ASP.NET feature is installed.
<modules> <add name="HttpCacheModule" /> <add name="StaticCompressionModule" /> <add name="DefaultDocumentModule" /> <add name="DirectoryListingModule" /> ... <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" preCondition="managedHandler" /> <add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" preCondition="managedHandler" /> <add name="RoleManager" type="System.Web.Security.RoleManagerModule" preCondition="managedHandler" /> ... </modules>
As you can see, this section contains both native modules that are simply identified by the name attribute, and managed modules that also specify a type attribute. For each application, the server resolves the enabled modules by looking up the native modules’ names in the globalModules section and directly loading the specified .NET type for managed modules. The type is the fully qualified .NET type name that refers to the class that implements this module and resolves to an assembly that is packaged with the application or an assembly installed in the machine’s Global Assembly Cache (GAC).
Deploying Assemblies Containing Managed Modules
Managed modules are classes implemented inside .NET assemblies. To support delegated deployment of managed modules, the server provides several options, as shown in Table 12-3, for deploying the module assemblies so that they can be added both globally on the server and for a specific application only.
Table 12-3 Managed Modules and Deployment Options
Deployment Option |
Assembly Location |
Module Registration Location |
Server |
Global Assembly Cache (GAC) |
Server level modules section in ApplicationHost.config |
Application |
Assembly in application’s /BIN directory OR Source code in application’s /App_Code directory |
Application’s modules section in application root’s Web.config |
Deploying the Module Assembly at the Server Level
If the module is to be installed globally for all applications on the server, it needs to be registered with the machine’s Global Assembly Cache (GAC). Before the managed assembly can be deployed to the GAC, it needs to be strongly signed by the developer (for more information on strongly signing .NET assemblies, see http://msdn2.microsoft.com/en-us/library/xc31ft41.aspx). In particular, Microsoft Visual Studio makes the signing process simple. Then, the managed assembly can be added to the GAC by running the following command.
gacutil.exe /if AssemblyPath
After your assembly is added to the Global Assembly Cache, you can add any of the modules it contains to the server level modules section by specifying their type. This type name must be fully qualified; that is, it must contain the full namespace path to the class (for example, System.Web.Security.FormsAuthenticationModule). Because when it creates your module, ASP.NET needs to locate an assembly that contains this type, the assembly must either be listed in the system.web/compilation/assemblies configuration collection or included in the type name by using the strong name notation. Here is an example of a strong name for the built-in FormsAuthentication module.
System.Web.Security.FormsAuthenticationModule, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=x86
You may wonder why the default module entries for ASP.NET modules do not specify the strong names and simply specify the fully qualified type names. This is because their parent assembly, System.Web.dll, is configured to be automatically preloaded by the ASP.NET applications (by being listed in the system.web/compilation/assemblies configuration collection in .NET Framework’s root Web.config). Thus, ASP.NET can locate the types of built-in ASP.NET modules by searching the preloaded assemblies, without having to specify the assembly signature in the module type string.
Deploying the Module Assembly with the Application
If the module is to be available in a specific application only, it can be xcopy-deployed with that application without registering anything globally on the server. In this case, the application owner can provide the module in two ways: as a compiled .NET assembly DLL in the /BIN subdirectory of the application root or as a source code file in the /App_Code subdirectory of the application root.
The /App_Code deployment model is more appropriate for development and test environments, because it enables editing of the module source code on the live server without recompiling the module DLL. The /BIN deployment model is recommended for production servers, because it does not require run-time compilation of the assembly and provides a more compact way to deploy large codebases than source code does.
Because the module type deployed inside the application is available only in the application, it can be used only in that application (unlike assemblies placed in the Global Assembly Cache, which are available to all applications on the server). To add the module, you simply need to add the fully qualified type into the modules configuration section in the application’s root Web.config file. For modules whose assemblies are in the /BIN directory, you can optionally specify the assembly name, although it is not necessary—ASP.NET by default preloads all /BIN assemblies. This is also true for modules that are deployed as source code to the /App_Code directory, because ASP.NET automatically compiles and loads it.
Packaging IIS 7.0 managed modules in the application is a powerful way to create self-contained applications that can be xcopy-deployed to a server and immediately function without globally installing any functionality on the server.
Be sure to also read the section titled “Locking Down Extensibility” later in this chapter to understand the security impact of allowing managed module delegation and how to properly control it.
Uninstalling Managed Modules
Unlike native modules, you can install managed modules simply by adding them to the modules list. Therefore, uninstalling managed modules is identical to disabling them and requires a single step.
Managed modules installed as part of ASP.NET installation cannot be individually uninstalled using Windows Setup (Windows Vista) or Server Manager (Windows Server 2008). So, if you need to remove any one of them, you have to do so by manually removing their entries from the modules section. Be sure to make a configuration backup in case you need to restore the original configuration later.
The section titled “Controlling What Modules Are Enabled” later in this chapter discusses removing managed modules. Look in the sections titled “Using IIS Manager to Install and Manage Modules” and ”Using Appcmd to Install and Manage Modules” later in this chapter to find steps detailing how you can use IIS Manager or the Appcmd command line tool to remove managed modules.
Understanding Module Preconditions
The modular architecture of IIS 7.0 relies heavily on controlling which modules are installed and enabled on the server and at the application level. Sometimes, making this determination based on static configuration is not sufficient, and the decision to use the module in a specific scenario must be made based on factors known only at run time. To support this functionality, IIS 7.0 introduces the concept of preconditions, which are configured conditions that the server evaluates at run time to determine whether a particular module should be used.
The following types of preconditions are supported:
Module load preconditions These preconditions may be associated with each installed native module in the globalModules configuration section, and they determine whether a particular module is loaded by each worker process when it starts. If any of the preconditions do not evaluate to true, the module is not loaded in the worker process. These preconditions can also be used in the isapiFilters configuration section to control the loading of ISAPI filters.
Module enablement preconditions These preconditions may be associated with each enabled module in the modules configuration section, and they determine whether the module is enabled for a particular application (or request). If any of the preconditions do not evaluate to true, the module does not run.
Handler mapping preconditions These preconditions may be associated with each handler mapping in the handlers configuration section, and they determine whether this handler mapping is considered when mapping a request to handlers. If any of the preconditions do not evaluate to true, the handler mapping is ignored.
In each case, one or more precondition strings may be specified to allow the configuration entry to be selectively used in cases where all of the specified preconditions evaluate to true. If any of the preconditions fail, the module is not loaded or enabled, or the handler mapping is not considered, depending on the scenario in which the precondition is being used. Here is an example of ASP.NET setup using preconditions to load the “ManagedEngine” native module only in application pools that use Integrated mode, run Framework version 2.0, and are configured to execute in a 32-bit mode.
<globalModules> ... <add name="ManagedEngine" image="%windir%\Microsoft.NET\Framework\v2.0.50727\webengine.dll" preCondition="integratedMode,runtimeVersionv2.0,bitness32" /> </globalModules>
Table 12-4 lists the supported precondition strings and scenarios in which they can be used.
Table 12-4 Precondition Strings
Precondition |
Applicable To |
bitness32, bitness64 Matches the “bitness” of the application pool |
globalModules, isapiFilters, modules, handlers |
classicMode, integratedMode Matches the configured managed pipeline mode of the application pool |
globalModules, isapiFilters, modules, handlers |
runtimeVersionv1.1, runtimeVersionv2.0 Matches the configured .NET run-time version of the application pool |
globalModules, isapiFilters, modules, handlers |
appPoolName=Name, appPoolName!=Name Matches the application pool name; this precondition can be used to selectively load a native module into a specific application pool |
globalModules, isapiFilters, modules, handlers |
managedHandler Matches requests to handler mappings with managed handlers |
modules only |
The bitness32 and bitness64 preconditions match the bitness of the worker process and can be used to selectively load modules in 32-bit or 64-bit application pools. In mixed 32-bit and 64-bit IIS environments, it may be necessary to load 32-bit native modules only in 32-bit application pools, because IIS will fail to load the 32-bit native DLLs into the 64-bit worker process. To help with this, the 32-bit native modules should configure the bitness32 precondition, which selectively loads them in 32-bit application pools only. For more information about running IIS in mixed 32-bit and 64-bit, please refer to the section titled “Installing Modules for x64 Environments” later in this chapter.
The classicMode and integratedMode preconditions match the configured managedPipelineMode attribute of each application pool. Together with the runtimeVersion preconditions, they provide a foundation for the ASP.NET versioning in IIS 7.0 and also allow for selecting the right set of ASP.NET handler mappings based on the integration mode of the application pool. In application pools that either use the Classic ASP.NET integration mode or use a .NET version that does not support direct integration, IIS 7.0 uses legacy ISAPI-based handler mappings for ASP.NET. Both of these sets of handler mappings are configured at the server level, and they use the classicMode/integratedMode and runtimeVersion preconditions to automatically select the right set of handler mappings based on the application pool’s managed pipeline mode and Framework version.
You can use the applicationPoolName precondition to selectively load/enable modules and handler mappings in a particular application pool. An IIS 7.0 mechanism is provided to enable specific customer scenarios primarily on shared Web hosting servers. IIS 7.0 does not use it by default.
Finally, the managedHandler precondition enables modules to be enabled only for requests to ASP.NET handlers. For ASP.NET Integrated mode applications, IIS 7.0 enables managed modules to execute for all requests, whether or not they are mapped to ASP.NET handlers. However, by default, all ASP.NET modules use the managedHandler precondition to run only for requests to managed handlers. This also enables the ASP.NET appdomain creation to be delayed until the first request to an ASP.NET handler is made. This precondition can be removed from each module to allow it to run for all content types, regardless of whether they are managed or native. For example, to allow ASP.NET Forms-based authentication to occur for all content on the site, you need to remove the managedHandler precondition from the “FormsAuthentication” module. You can learn more about this in the “Enabling Managed Modules to Run for All Requests” section further in this chapter.
Preconditions solve a number of key problems in IIS 7.0. However, they can also add management complexity, and if configured incorrectly, they can result in unintended behavior. The largest cause of precondition-related problems is due to preconditions preventing modules from being loaded/enabled or handler mappings from being used, resulting in missing functionality. Though the module or handler mapping may appear present, its precondition can be preventing it from being active. These types of problems may be hard to diagnose because missing functionality does not always manifest in errors.
Another common problem is precondition inconsistency, where related configuration is not preconditioned correctly and results in configuration errors. For example, if a native module has a bitness32 load precondition, but the corresponding enablement entry in the modules list does not, requests to 64-bit application pools will produce a “bad module” error because the module being enabled is not loaded. Likewise, if a handler mapping refers to a module whose enablement precondition in the modules list prevents it from being enabled, requests that are mapped to this handler mapping will encounter an error.
To avoid these problems, remember that preconditions primarily serve to prevent a module/handler mapping from being used in scenarios where it cannot function. Make sure that the preconditions do not restrict the module from being available in scenarios where it’s needed.
Also keep in mind the precondition relationships between the different configuration sections where they exist. Preconditions must get more restrictive as they go from module load preconditions, to module enablement preconditions, and finally to the handler mapping precondition for the module. For example, if the module load precondition in globalModules is “bitness32”, the module enablement precondition for the corresponding entry in the modules section must at least contain that precondition. If the module is referenced in a handler mapping in the handlers configuration, the precondition of that entry must contain at least the precondition strings from the modules entry (which in turn contains at least the preconditions from globalModules entry).
Installing Modules for x64 Environments
When IIS 7.0 is installed on 64-bit versions of the operating system, it functions in native 64-bit mode by default. This means that all application pools create native 64-bit worker processes and load 64-bit IIS core engine components and modules. However, by allowing any of its application pools to use the 32-bit emulation mode called wow64, IIS 7.0 supports hosting both native 64-bit and 32-bit applications. Unlike IIS 6.0, which also provided the ability to use wow64, IIS 7.0 allows each application pool to configure this individually, enabling side by side hosting of native 64-bit and 32-bit applications on the same server.
Each application pool that has the enable32BitAppOnWin64 property set to true will create 32-bit worker processes and load the 32-bit version of the IIS core and modules. This is possible because IIS setup on 64-bit operating systems installs both 64-bit and 32-bit versions of all IIS components and modules—the native 64-bit versions go into the standard %windir%\System32\Inetsrv directory, and the 32-bit versions go into the %windir%\Syswow64\Inetsrv directory. At run time, when IIS tries to load modules located in the %windir%\System32\Inetsrv directory in a 32-bit wow64 worker process, the wow64 file system redirection feature automatically redirects the file access to the \Syswow64 directory where the 32-bit versions of the DLLs are located.
This mechanism enables IIS or third-party modules installed under the system32 directory to provide 32-bit versions under the \Syswow64 directory and then automatically load the correct version based on the “bitness” of the worker process.
However, the entire reason mixed 64-bit and 32-bit environments exist is that some functionality may not be available in native 64-bit flavors, requiring the worker process to operate in 32-bit mode. This is often needed for ASP applications that invoke in-process 32-bit COM components, 32-bit only ISAPI filters, or 32-bit only native modules. Likewise, some components may be available only in 64-bit flavors, and therefore they are not supported in 32-bit worker processes. To support such a scenario, you must be able to install native modules, ISAPI filters, and ISAPI extensions so that IIS never attempts to load a 32-bit component in a 64-bit worker process, and vice versa. IIS 7.0 provides this support via the bitness preconditions (see the section titled “Understanding Module Preconditions” earlier in this chapter), which enable native modules, ISAPI filters, and handler mappings to indicate whether they are available only in 32-bit or 64-bit application pools.
For example, handler mappings that map requests to the 32-bit version of the ASP.NET ISAPI use the bitness32 precondition to insure that they are used only inside 32-bit worker processes.
<handlers accessPolicy="Read, Script"> <add name="PageHandlerFactory-ISAPI-2.0" path="*.aspx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi dll" preCondition="classicMode,runtimeVersionv2.0,bitness32" responseBufferLimit="0" /> ... </handlers>
By default, the 64-bit version of the .NET Framework also registers an identical mapping to the 64-bit version of the aspnet_isapi.dll, which uses the bitness64 precondition so that it is selected only in 64-bit worker processes.
Using the bitness32 and bitness64 preconditions can therefore allow native modules, ISAPI filters, and ISAPI extensions specified in the handler mapping configuration to directly target 64-bit or 32-bit application pools, without using the file system redirection mechanism to provide both 32-bit and 64-bit flavors.
Common Module Management Tasks
Besides enabling you to choose which modules are installed on the server, IIS 7.0 enables you to further fine-tune its functionality by selecting which modules are enabled on the server or even for a particular application. Furthermore, you will sometimes want to tweak other aspects of module operation, such as their relative order, or the specific scenarios in which modules should execute. This section will illustrate some of these common module management tasks.
Controlling What Modules Are Enabled
Despite the differences between installation procedures for native and managed modules, the modules configuration section provides a unified view of the enabled modules. By manipulating the module entries in the modules section, you can control which modules will be allowed to function on the server by default or for a specific application:
Adding a module at the server level allows it to execute by default in all applications on the server, except for applications that specifically remove it.
Removing a module at the server level prevents it from executing in all applications on the server, except for applications that specifically add it back.
Adding a module at the application level allows it to execute in that specific application.
Removing a server-level defined module at the application level removes this module from the specific application, while allowing other applications to use it.
In a nutshell, modules that are enabled on the server level provide a default feature set for all applications on the server. Each application can then tweak this feature set by removing unneeded modules and then adding additional modules in its modules section. It is important to remember that though you can add new managed modules at the application level, new native modules must be installed at the server level to be enabled at the application level. This means that applications cannot introduce new native modules—they can only remove existing ones that are enabled, or add back native modules that are installed but not enabled by default at the server level.
Enabling Managed Modules to Run for All Requests
The ability to extend IIS with managed modules that execute for all content types is one of the central breakthroughs of IIS 7.0. However, for backward compatibility reasons, all of the built-in ASP.NET modules are configured to execute only for requests to managed (ASP.NET) handlers. Because of this, useful ASP.NET services such as Forms Authentication are by default available only for requests to ASP.NET content types, and they are not applied to requests to static content or ASP pages. The ASP.NET setup does this, adding the “managedHandler” precondition to each ASP.NET module element when it is added to the modules configuration section. See the section titled “Understanding Module Preconditions” earlier in this chapter for more information.
Because of this, it is necessary to remove this precondition from each ASP.NET module whose service is desired for all application content. This can be done by using Appcmd or IIS Manager to edit the specified modules element, or by manually removing the precondition from the module element. When this is desired at the application level for a module element inherited from the server level configuration, it is necessary to remove and redefine the module element without the precondition.
<modules> <remove name="FormsAuthentication" /> <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" /> </modules>
This clears the default “managedHandler” value of the preCondition attribute and enables the FormsAuthentication module to run for all requests.
When you use IIS Manager or Appcmd to edit the module element, this configuration is automatically generated whenever you make changes at the application level.
Alternatively, you can configure your application to ignore all managedHandler preconditions and effectively always execute all managed modules for all requests without needing to remove the precondition for each one. This is done by setting the runAllManagedModulesForAllRequests configuration option in the modules configuration section.
<modules runAllManagedModulesForAllRequests="true" />
Controlling Module Ordering
Due to the pipeline model of module execution, module ordering is often important to ensure that the server “behaves” as it should. For example, modules that attempt to determine the authenticated user must execute before modules that verify access to the requested resource, because the latter needs to know what the authenticated user is. This ordering is almost always enforced by the stages of the request processing pipeline. By doing their work during the right stage, modules automatically avoid ordering problems. However, in some cases, two or more modules that perform a similar task—and therefore execute in the same stage—may have ordering dependencies. One prominent example is built-in authentication modules. They are run during the AuthenticateRequest stage, and to authenticate the request with the strongest credentials available, they should be in the strongest to weakest order. To resolve such relative ordering dependencies, the administrator can control the relative ordering of modules by changing the order in which they are listed in the modules section.
This works because the server uses the order in the modules configuration section to order module execution within each request processing stage. By placing module A before module B in the list, you can allow module A to execute before module B.
This also means that when an application enables a new module (by adding a new managed module, or enabling a native module that was not previously enabled), that module is listed after the modules enabled by higher configuration levels due to the configuration collection inheritance. This can sometimes be a problem if the new module should run before an existing module defined at the higher level, because the configuration system does not provide a way to reorder inherited elements. In this case, the only solution is to clear the modules collection and re-add all of the elements in the correct order at the application level.
<modules> <clear/> <add name="HttpCacheModule" /> ... <add name="MyNewModule" type="Modules.MyNewModule" /> ... <modules>
Adding Handler Mappings
Though modules typically execute for all requests so that the modules can provide a content-independent service, some modules may opt to act as handlers. Handlers are responsible for producing a response for a specific content type and are mapped in the IIS 7.0 handler mapping configuration to a specific verb/extension combination. For handlers, the server is responsible for mapping the correct handler based on the handler mapping configuration, and they are also responsible for invoking that handler during the ExecuteRequest request processing stage to produce the response for this request. Examples of handlers include StaticFileModule, which serves static files; DirectoryListingModule, which displays directory listings; and the ASP.NET PageHandler, which compiles and executes ASP.NET pages.
The main conceptual difference between modules and handlers is that the server picks the handler to produce the response for requests to a specific resource, whereas modules typically process all requests in a resource-independent way and typically do not produce responses. Because of this, only the one handler mapped by the server is executed per request. If you are familiar with IIS 6.0, this is similar to the distinction between the ISAPI extensions, which provide processing for a specific extension, and ISAPI filters, which intercept all requests.
Traditionally, most application frameworks including ASP.NET, ASP, PHP, and ColdFusion are implemented as handlers that process URLs with specific extensions.
You register a handler on the server by creating a handler mapping entry in the collection located in the system.webServer/handlers configuration section. This concept is similar to the script maps configuration in previous releases of IIS, but in IIS 7.0 it is extended to allow for more flexibility and to accommodate more handler types. For applications using the Integrated mode, this section also supports managed handlers that in previous IIS versions are registered in the ASP.NET httpHandlers configuration section.
After it receives the request, the server examines the collection of handler mappings configured for the request URL and selects the first handler mapping whose path mask and verb match the request. Later, during the ExecuteRequestHandler stage, the handler mapping will be used to invoke a module to handle the request.
Each handler mapping collection entry can specify the attributes shown in Table 12-5.
Table 12-5 Attributes Specified by Handler Mappings
Attribute |
Description |
name (required) |
The name for the handler mapping. |
path (required) |
The path mask that must match the request URL so that this handler mapping can be selected. |
verb (required) |
The verb list that must match the request verb so that this handler mapping can be selected. |
resourceType |
Whether the physical resource mapped to the request URL must be an existing file, directory, either, or unspecified (if the physical resource does not have to exist). |
requireAccess |
The accessFlag level that is required for this handler to execute. |
precondition |
The precondition that determines if this handler mapping is considered. |
allowPathInfo |
Whether or not the PATH_INFO / PATH_TRANSLATED server variables contain the path info segment; may cause security vulnerabilities in some CGI programs or ISAPI extensions that handle path info incorrectly. |
responseBufferLimit |
The maximum number of bytes of the response to buffer for this handler mapping. Response buffering is new in IIS 7.0 and enables modules to manipulate response data before it is sent to the client. The default is 4 MB, although ISAPI extensions installed with legacy APIs will have it automatically set to 0 for backward compatibility reasons. |
Modules |
List of modules that attempt to handle the request when this mapping is selected. |
scriptProcessor |
Additional information that is passed to the module to specify how the handler mapping should behave. Used by ISAPI extension module, CGI module, and FastCGI module. |
type |
The managed handler type that handles the request when this mapping is selected. |
The information in the handler mapping is used as follows.
The precondition is first used to determine if the handler mapping is to be used in a particular application pool. If any of the preconditions fail, the mapping is ignored.
The path and verb are matched against the request URL and verb. The first mapping that matches is chosen. If no mappings matched, a “404.4 Not Found” error is generated.
If the accessPolicy configuration does not meet the requireAccess requirement for the handler mapping, a “403 Access Denied” error is generated.
If the resourceType is set to File, Directory, or Either, the server makes sure that the physical resource exists and is of the specified type. If not, a “404 Not Found” error is generated. Also, check that the authenticated user is allowed to access the mapped file system resource. If resourceType is set to Unspecified, these checks are not performed.
Even though the majority of IIS 7.0 handlers are added at the server level and inherited by all applications on the server, you can specify additional handlers at any level. Handler mappings added at a lower level are processed first when matching handler mappings, so new handlers may override handlers previously declared at a higher configuration level. Because of this, if you want to remap that path/verb pair to another handler for your application, it is not necessary to remove a handler added at a server level—simply adding that handler mapping in your application’s configuration does the job.
Types of Handler Mappings
Though it provides a standard way to map handlers to requests, the handlers configuration also supports a number of different types of handlers, as shown in Table 12-6.
Table 12-6 Handler Types
Handler Type |
Configuration |
IIS 7.0 Examples |
Native module(s) The module must support the ExecuteRequestHandler event |
modules specifies the list of native modules that will handle this request (typically just specifies one module) |
TraceVerbHandler, OptionsVerbHandler, StaticFileModule, DefaultDocumentModule, DirectoryBrowsingModule |
ASP.NET handler The application must be using the Integrated ASP.NET mode |
type specifies fully qualified .NET type that implements ASP.NET handler interfaces |
ASP.NET PageHandlerFactory (aspx pages), ASP.NET WebResourceHandler |
ISAPI extension |
modules specifies the ISAPIModule; scriptProcessor specifies the path to the ISAPI extension DLL to load |
ASP.dll (asp pages) |
CGI program |
modules specifies the CGIModule; scriptProcessor specifies the path to the CGI executable |
Any CGI executable |
FastCGI program |
modules specifies the FastCGIModule; scriptProcessor specifies the path and arguments for a FastCGI executable registered in the FastCGI configuration section |
Any FastCGI executable (such as PHP-CGI.EXE) |
Unlike script maps in previous versions of IIS, which provide hardcoded support for ISAPI extensions and CGI programs, IIS 7.0 hardcodes nothing—all types of handlers are implemented on top of the standard native or managed module API. IIS 7.0 supports ISAPI extensions by hosting them with the ISAPIModule, supports CGI programs with the CGI module, and features new support for FastCGI programs with FastCgiModule. The IsapiModule, CgiModule, and FastCgiModule modules are all native modules, much like StaticFileModule, except they support interfacing with external handler frameworks to handle the request, using the ISAPI, CGI, and FastCGI protocols respectively.
If you look at the handler mappings created by default by a full IIS 7.0 install, you will see some of the following.
<handlers accessPolicy="Read, Script"> <add name="ASPClassic" path="*.asp" verb="GET,HEAD,POST" modules="IsapiModule" scriptProcessor="%windir%\system32\inetsrv\asp.dll" esourceType="File" /> <add name="ISAPI-dll" path="*.dll" verb="*" modules="IsapiModule" resourceType="File" requireAccess="Execute" allowPathInfo="true" /> ... <add name="PageHandlerFactory-Integrated" path="*.aspx" verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.PageHandlerFactory" preCondition="integratedMode" /> ... <add name="PageHandlerFactory-ISAPI-2.0" path="*.aspx" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi dll" preCondition="classicMode,runtimeVersionv2.0,bitness32" responseBufferLimit="0" /> ... <add name="StaticFile" path="*" verb="*" modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule" resourceType="Either" requireAccess="Read" /> </handlers>
This configuration fragment shows a good cross-section of the kinds of handler mappings that you can create. First is IsapiModule handler mapping, which enables ASP pages to be executed with the ASP.dll ISAPI extension. Second is the IsapiModule mapping, which supports direct requests to ISAPI extensions located in the application directories, which require the Execute permission.
Then, you see two mappings for the ASP.NET PageHandlerFactory, which supports the processing of ASPX pages. The first mapping uses the aspnet_isapi.dll ISAPI extension to process the request, and the second uses the Integrated mode for executing ASP.NET handlers directly. Each of these mappings uses a precondition to make sure that only one of the mappings is active in each application pool based on the ASP.NET integration mode. Classic mode application pools use the ISAPI mapping, and Integrated mode application pools use the integrated mapping. You can read more about ASP.NET integration and ASP.NET handler mappings in Chapter 11. Finally, you see the static file handler mapping, designed to be a catch-all mapping that is mapped to all requests that do not match any of the other handler mappings by specifying “*” for both path and verb. This is similar to previous versions of IIS where any requests not mapped to an ISAPI extension scriptmap are handled by the static file handler in IIS. This mapping also illustrates letting multiple modules attempt to handle the request as part of a single handler mapping. First, StaticFileModule attempts to serve a physical file if one is present, then DefaultDocumentModule performs the default document redirect, and finally DirectoryBrowsingModule attempts to serve a directory listing.
You will find out more about using IIS Manager and Appcmd to create handler mappings in the sections titled “Using IIS Manager to Install and Manage Modules” below and “Using Appcmd to Install and Manage Modules” later in this chapter.
Using IIS Manager to Install and Manage Modules
IIS Manager provides a powerful UI for managing modules on the server. This UI can be used to install both native and managed modules, as well as manage enabled modules on the server and for specific applications.
The Modules feature provides this functionality, and it can be accessed at two separate levels for slightly different functionality:
By server administrators at the server level, to install native modules on the server, add new managed modules, and configure modules that are enabled on the server by default.
By server or site administrators at the application level, to add new managed modules and configure enabled modules for the application.
At the server level, you can select the machine node in the tree view and then double-click the modules to access the Modules feature, as shown in Figure 12-3.
Figure 12-3 The Modules feature in IIS Manager.
You will see the list of modules enabled at the server level, which corresponds to the list of modules in the modules configuration section at the server level in ApplicationHost.config. For each module, you will see its name. For native modules, you’ll also see the path to the image DLL. For managed modules, you’ll also see the module type name. You will also see three actions available on this page:
Add Managed Module Enables you to add a new managed module to the list of enabled modules. In the resulting dialog box, you can specify the name of your new module, as well as the module’s type.
You can select a module type from the Type drop-down list, which contains all module types available from the assemblies located in the machine’s Global Assembly Cache listed in the system.web/compilation/assemblies section of the .NET Framework’s root Web.config. You can also type in your own type if it’s not yet listed. Select the Invoke Only For Requests To ASP.NET Applications Or Managed Handlers check box if you want your module to use the managedHandler precondition and only execute for requests to ASP.NET handlers (see the section titled “Understanding Module Preconditions” for more information about this).
Configure Native Modules Enables you to enable an already installed native module, install a new native module, or uninstall an existing native module.
Here, you can enable native modules that are installed but not currently enabled by selecting one or more of them. You can also use the Register button to install a new native module, the Edit button to edit the name or the path for an installed module, or the Remove button to uninstall the selected native module.
View Ordered List Enables you to display the list of modules in an ordered list so that you can adjust their sequence by using the Move Up and Move Down actions. When this is done at the server level, you can reorder the modules without resorting to clearing the modules collection (see the section titled “Controlling Module Ordering” earlier in this chapter for more information).
Also, when you select a module entry in the list, three additional actions become available:
Edit. This action enables you to edit the modules entry. For native modules, you can directly change the native module installation information including its name and path to native image DLL. For managed modules, you can edit the module name and the module type.
Lock/Unlock. These actions enable you to lock the specific module item at the server level, such that it cannot be removed or modified at the application level. See the section titled “Locking Down Extensibility” later in this chapter for more information about locking modules.
Remove. This action enables you to remove the module entry. For native modules, this disables the module by default. For managed modules, this removes the module entry, requiring you to later re-add this entry at the application level to enable it there.
To access the Modules feature at the application level, go to the tree view and select the application you would like to administer. Then double-click the Modules feature icon. You will be presented with the same view as before, except for the following differences:
You will no longer be able to add new native modules from the Configure Native Modules dialog box. Remember, this is because native modules are installed for the entire server, and you must have Administrative privileges to do that. Instead, you will only be able to enable already installed native modules that are not currently enabled for your application.
You will no longer be able to edit native module information or edit managed module information for managed modules that are enabled at the server level (you can still edit module information for managed modules added in your application).
You will not be able to lock/unlock modules.
When adding managed modules, the tool will also inspect all assemblies in the /BIN and /App_Code source files for possible modules to add.
You can use the Revert To Inherited action to discard whatever changes were made to the modules configuration section at the application level and then revert to the default module configuration at the server level.
Despite these limitations, site administrators can still use IIS Manager to install new managed modules or manage their applications’ module feature set without requiring Administrative privileges on the machine. This is especially valuable given the ability of IIS Manager to enable remote delegated administration for application owners. Of course, server administrators can also benefit from IIS Manager for unrestricted module management.
Using IIS Manager to Create and Manage Handler Mappings
IIS Manager also provides a convenient interface to manage handler mappings, thus removing some of the complexity involved with editing the handler mappings manually. This functionality is provided by the Handler Mappings feature, which both server administrators and site administrators can access.
After selecting the node at which you’d like to manage handler mappings, you can select the feature by double-clicking the Handler Mappings icon. This presents the Handler Mappings view, as shown in Figure 12-4.
Figure 12-4 The Handler Mappings feature in IIS Manager.
Here, you will see a list of handler mappings including the handler mapping name, path, and other useful information. The Handler column provides a summary of how the handler is implemented, showing either the modules list for native handlers or the type for managed handlers. The Path Type indicates the resourceType of the handler mapping. The State column indicates if this handler is enabled (this applies only to handler mappings using IsapiModule or CgiModule) and indicates whether the ISAPI extension or CGI program specified by the mapping is enabled in the system.webServer/security/isapiCgiRestrictions configuration (analogous to the Web Service Restriction List in IIS 6.0).
The tool provides a number of ways to add new handler mappings, breaking them out by type to simplify handler mapping creation:
Add Managed Handler This action enables you to create handler mapping to an ASP.NET handler. This mapping is available only in application pools that are using Integrated mode. It is shown in Figure 12-5.
Figure 12-5 Add Managed Handler dialog box.
In this dialog box, you specify the path mask for the handler mapping, as well as the ASP.NET handler type that should provide processing for it. Much like when adding managed modules, the tool searches for usable types in the assemblies from the Global Assembly Cache (GAC) that are referenced in the ASP.NET compilation/assemblies collection. It also searches for application assemblies when you’re adding the handler at the application level. You can use the Request Restrictions dialog box to also specify the verb list for the handler mapping (otherwise, it defaults to “*”), restrict the mapping to physical resources such as a file or directory (the default is unspecified), and set access level as required to execute the mapping (defaults to Script). You should indeed set these to the strictest possible levels for added security instead of leaving them at default values.
Add Script Map This action enables you to create an ISAPI extension or CGI program handler mapping, similar to the IIS 6.0 scriptmap. This is shown in Figure 12-6.
Figure 12-6 Add Script Map dialog box.
This is similar to the previous dialog box, except instead of the .NET handler type, you select the ISAPI extension, DLL, or CGI program executable on the server’s local file system. This dialog box also prompts you to automatically create the isapiCgiRestriction entry to allow the ISAPI extension or CGI program to execute on the server.
Add Wildcard Script Map This is identical to the Add Script Map dialog box, except that it enables you to create a wildcard script map that intercepts all requests.
Add Module Mapping This is the most interesting action, because it enables you to create a general handler mapping that specifies one or more native modules and optionally a script processor. You can use this dialog box to create ISAPI extension, CGI, and FastCGI handler mappings in addition to simple handler mappings for native modules, as shown in Figure 12-7.
Figure 12-7 Add Module Mapping dialog box.
The dialog box enables you to specify the Module (and it displays a list of available native modules, although not all of them can function as handlers) and the Executable (the script processor). It also provides special handling for IsapiModule, CgiModule, and FastCgiModule handler mappings by prompting you to automatically create the corresponding isapiCgiRestrictions or FastCGI configuration needed for the mapping to work correctly.
In addition to adding new handler mappings, you can also edit or delete existing handler mappings by clicking the item in the list and using the Edit or Delete commands that become available. At the server level, you can also lock handler mappings to prevent them from being removed at the lower level, although this has less effect than locking modules because handler mappings can be overridden by adding new handler mappings for the same path/verb.
You can also use the “View Ordered List” action to order handler mappings. Keep in mind that just as it is for modules, ordering inherited elements requires the tool to use <clear/> to clear the handlers collection and add again all parent items into the configuration level being edited, essentially orphaning the configuration from the parent handler mappings configuration.
If you edit handler mappings as a site administrator, you have virtually the same functionality available to you for managing the handler mappings for your site, application, or any URL inside your site. However, the tool will not prompt you to enable ISAPI extensions, CGI programs, and FastCGI programs that the administrator has not already enabled at the server level.
Using Appcmd to Install and Manage Modules
The Appcmd command line tool also provides top-level support for managing modules. To begin with, you can always edit the IIS configuration via the Appcmd Config object to perform all the tasks necessary to install and manage modules. However, the tool also provides a module object, which directly supports common module management tasks. This is why Windows Setup calls into Appcmd to install modules and is the reason Appcmd is often the quickest way to install and manage modules. Appcmd is also the only tool available for managing modules on Windows Server 2008 Server Core installations, which do not support IIS Manager.
Appcmd is located in the %windir%\System32\Inetsrv directory, which is not present in the PATH variable by default, so you need to use the full path to the tool to run it.
%windir%\system32\inetsrv\AppCmd
You must be logged in as an administrator when using Appcmd. Also, be sure to execute Appcmd commands from an elevated command line prompt (click Start, right-click Command Prompt, and choose Run As Administrator). You can refer to Chapter 7, “Using Command Line Tools,” for more information about the tool. If you get lost while learning Appcmd, be sure to check out the built-in command line help, which provides parameters and examples.
AppCmd module /? - See all commands on the module object AppCmd install module /? - See the usage help for the install module command
In the help output for the MODULE object, you can see that the tool supports the following commands:
List Enables you to examine enabled modules at the server level or for a specific application.
Install Enables you to install new native modules on the server.
Uninstall Enables you to uninstall currently installed native modules on the server.
Add Enables you to add a new module. You can also opt to enable an installed native module for a specific application.
Delete Enables you to disable a managed module You can also opt to disable a native module, optionally for a specific application.
Set Enables you to edit specific entries in the modules configuration section for the purposes of changing the name or preconditions of a module or editing type information for managed modules.
In the next section, we’ll provide some examples of using these commands to manage modules with Appcmd.
Installing and Uninstalling Modules
The Install and Uninstall module commands provide support for—you guessed it—installing and uninstalling native modules on the server.
To install native modules, you can use the following syntax.
AppCmd Install Module /name:string /image:string [/precondition:string] [/add:bool] [/lock:bool]
This command accepts the parameters shown in Table 12-7.
Table 12-7 Appcmd Install Module Parameters
Parameter |
Description |
name (required) |
Module name. |
image (required) |
The path to the module DLL. |
preCondition |
Optionally specifies the list of valid load preconditions for the module. If specified, controls whether the module is loaded by the IIS worker processes in particular application pools. The default is empty. |
add |
Optionally specifies whether to also enable the module at the server level. The default is TRUE. |
lock |
Optionally specifies whether to also lock the module entry so that it cannot be disabled by applications. This only applies if the module is being enabled. The default is FALSE. |
The simplest case is installing a module by simply specifying its name and image path.
AppCmd install module /name:MyNativeModule /image:c:\modules\mymodule.dll
This installs and automatically enables the new module at the server level so that it’s loaded by all IIS worker processes and enabled for all applications by default. If you wanted to install the module but enable it selectively for specific applications only, you would include the /add:false parameter. Then, you could use the Appcmd Add Module command later to enable it for a specific application by using the /app.name parameter. If, on the other hand, you wanted to install and enable this module and prevent applications from disabling it, you would include the /lock:true parameter for the install command. You could also set the precondition parameter to one of the supported values (see the section titled “Understanding Module Preconditions” earlier in this chapter) to control which application pools the module is loaded and available in.
To uninstall a module, you can use the following syntax.
AppCmd Uninstall Module ModuleName
This command accepts the module’s name as the identifier.
Here is an example of how you can uninstall the module you installed earlier.
AppCmd uninstall module MyNativeModule
Enabling and Disabling Modules
You can use the Add Module and Delete Module commands to manage the modules enabled on the server or for a particular application. You can also use the Add Module command to add new managed modules. These commands manipulate the modules configuration section, which contains the entries for enabled native modules and defines managed modules. Therefore, you can perform a number of different tasks around module management.
One of the most common uses for the Add Module command is to add new managed modules. Because native modules have to be installed on the server first before they are enabled, the Install Module command is more appropriate for installing and automatically enabling them. You can add a new managed module at the server level or for any particular application using the following syntax.
AppCmd Add Module /name:string [/type:string] [/precondition:string] [/app.name:string] [/lockItem:bool]
This command supports the parameters shown in Table 12-8.
Table 12-8 Appcmd Add Module Parameters
name (required) |
Module name. |
type (required) |
The fully qualified .NET type of the module. If the module is being added at the server level, the type must be in an assembly registered with the machine’s Global Assembly Cache. If it’s being added for a specific application, then it can also be deployed with the application. Refer to the section titled “Deploying Assemblies Containing Managed Modules” for more information. |
preCondition |
Optionally specifies the list of valid enablement preconditions for the module. If specified, controls whether the module is enabled in specific application pools and for specific requests. The default is empty. |
app.name |
Optionally specifies the application path for the modules to be added to. The default is empty, meaning the server level. |
lockItem |
Optionally specifies whether or not to lock the module entry against removal at lower configuration levels. |
For example, to add a new managed module at the server level, you can do the following.
AppCmd add module /name:MyManagedModule /type:MyModules.MyModule
This makes this module enabled by default for all applications on the server. If you wanted to add it only to the root application in the default Web site, you would also add the /app.name:"Default Web Site/” parameter. You could also set the preCondition parameter to one of the supported values (see the section titled “Understanding Module Preconditions” earlier in this chapter) to control for which application pools and requests the module is enabled.
You can also use the lockItem parameter just like you can when creating new configuration collection entries to lock the module entry, which prevents lower configuration levels from removing the module configuration entry. You can leverage this when adding new managed modules at the server level to prevent them from being disabled at the application level. This is discussed more in the section titled “Locking Down Extensibility” later in this chapter.
Another common use of the Add Module command is to enable a native module that is not currently enabled. For example, if you installed a native module with the /add:false parameter, resulting in it being installed but not enabled by default, you can directly enable it.
AppCmd add module /name:MyNativeModule
You can use the /app.name parameter here to specify the application where the module should be enabled. This only works for native modules; that is, when re-enabling managed modules for a specific application, you always have to specify the type because this information is not available elsewhere like it is for native modules.
You can use the Delete Module command to do the opposite—to disable a module that is currently enabled. You can also use this command to disable native module or managed modules at the server level, or for a specific application, using the following syntax.
AppCmd Delete Module ModuleName [/app.name:string]
For example, to disable a module that is enabled at the server level, you can do the following.
AppCmd delete module MyModule
This works for both managed and native modules, with a slight caveat: if you disable a native module, you can re-enable it simply by using the Add Module /name:ModuleName command. However, if you disable a managed module, you will need to specify its full type to re-enable it. If you delete a managed module at the level where it’s defined for the first time, you may lose the type information in case you need to re-add it later.
Examining Enabled Modules
In addition to installing/uninstalling and enabling/disabling modules, Appcmd supports examining the enabled modules with the LIST command. This can prove valuable when diagnosing module-related issues by enabling you to quickly determine which modules are enabled for a specific application, or if a specific module is enabled.
The List Module command, much like the LIST commands for other Appcmd objects, enables you to display all modules as well as query for modules that satisfy a specific query. In the simplest use, it enables you to quickly list the modules that are enabled at the server level.
AppCmd list modules
To see which modules are enabled for a specific application, use the /app.name:AppPath parameter to specify the application path for which the enabled modules should be displayed.
You can also specify queries for one or more of the module attributes, including precondition and type, to find modules that have that attribute. For example, to find all managed modules that have the managedHandler precondition set, you can use the following code.
AppCmd list modules "/type:$<>" "/precondition:$=*managedHandler*"
You can also look for specific modules by using the module name as the identifier.
AppCmd list module DefaultDocumentModule
Creating and Managing Handler Mappings
Though Appcmd provides a top-level Module object for managing modules, it does not provide a top-level view of the handler mappings configuration. However, you can always use the CONFIG object to directly manage the system.webServer/handlers configuration section to accomplish any needed handler mapping management task.
Discussing the full collection editing syntax of the CONFIG object is out of scope for this section (see Chapter 7), but here are some examples for using it to do basic handler mapping management tasks.
Adding a Handler Mapping
To add a handler mapping to a native module, run the following command on one line.
AppCmd set config /section:handlers "/+[name='TestHandler',path='*test', verb='GET,POST,HEAD',modules='TestModule']"
This adds a server-level handler mapping that maps GET, POST, and HEAD requests to *.test URLs to the “TestModule” native module. Though only the name, path, and verb attributes are required (and the modules attribute should be set for the native module handler mapping), you can also specify any of the other attributes that apply. You can also specify the configuration path at which this mapping should be added instead of adding it at the server level, as shown in the next example.
To add a handler mapping to a managed handler type, run the following command on one line.
AppCmd set config "Default Web Site/" /section:handlers "/+[name='ASPNHandler',path='*.aspn',verb='*', type='MyHandlers.ASPNHandler',precondition='integratedMode']"
This adds a handler mapping for the root of the “Default Web Site” Web site, which maps all requests to *.aspn URLs to the .NET MyHandlers.ASPNHandler handler type. Notice that the handler mapping is also preconditioned to be used only in application pools running in Integrated mode. This is required for handler mappings to managed types, because only Integrated mode supports adding managed handler types directly into IIS handler mappings.
Editing a Handler Mapping
To edit a handler mapping, use the following code.
AppCmd set config /section:handlers /[name='TestHandler'].verb:GET
This sets the verb attribute of the handler mapping identified by the name TestHandler to the new value of GET. Note that the name attribute serves as the unique collection key for the handlers configuration section.
You can use this syntax to edit any of the handler mapping attributes. You can also edit the handler mapping you created at the Default Web Site/ level by specifying that path after the SET CONFIG command.
Deleting a Handler Mapping
To delete a handler mapping, you can use the /- prefix for deleting configuration collection elements.
AppCmd set config /section:handlers /-[name='TestHandler']
This deletes the handler mapping you created earlier, identified by the name TestHandler. You can also delete the handler mapping you created at the “Default Web Site/” level by specifying that path after the SET CONFIG command.
Adding Entries to the ISAPI CGI Restriction List (Formerly Web Service Restriction List)
When creating handler mappings that use CgiModule or IsapiModule to support CGI programs or ISAPI extensions respectively, you also need to allow the specific CGI program or ISAPI extension by adding it to the ISAPI CGI Restriction List. In IIS 6.0, this is known as the Web Service Restriction List and is a key security measure to control the execution of third-party code on the server.
For example, to add an ISAPI extension DLL to the system.webServer/security/isapiCgiRestriction list, use the following command.
appcmd set config /section:isapiCgiRestriction /+[path='c:\myisapi.dll', allowed='true']
To allow (or disallow) an existing entry in the list, use the following command.
appcmd set config /section:isapiCgiRestriction /[path='c:\myisapi.dll'].allowed:true
To delete an entry in the list, use the following command.
appcmd set config /section:isapiCgiRestriction /-[path='c:\myisapi.dll']
You can specify both CGI programs (executables) and ISAPI extensions (DLLs) in this list.
Securing Web Server Modules
The extensibility architecture in IIS 7.0 is in many ways the recognition of the fact that it’s not really the server but rather the application that runs on it that makes all the difference. Unfortunately, history shows that it is also the application that is most commonly the cause of security vulnerabilities. The lockdown approach to security that IIS 6.0 provides—restricting the ability to run new code on the server and reducing the privilege of that code—has been immensely successful in reducing Web server exploits. Now, with IIS 7.0, server administrators must strike a balance between the functionality afforded by the new extensibility model and server security. Therefore, it is now more important than ever to understand the security impact of hosting server extensibility and to know how to properly lock it down to avoid weakening the security of your server.
When it comes to securing the server, one of the biggest problems administrators face today is dealing with system complexity and being able to properly apply key security best practices rather than being bogged down in the details. This approach, though not a replacement for proper security threat modeling and penetration testing at the application level, enables you to significantly reduce the general security risk to the server. Basically, you need to be able to answer the following question: Assuming you cannot trust the code running on your server to be completely foolproof, how can you control what it can and cannot do, and at the same time prevent your server from being compromised if this code is exploited?
The best answer to this question is to approach it in the following stages. First, you need to know how to reduce the server’s surface area to a minimum, removing all functionality that is not essential to its operation. Second, you need to understand the privilege of code that executes on the server and then reduce it as much as possible. Finally, you need to maintain control over the extensibility that is allowed on the server, preventing undesired functionality from being added or desired functionality from being removed. You should also apply the best practices listed in Chapter 14, “Implementing Security Strategies,” to secure individual features.
Taking Advantage of Componentization to Reduce the Security Surface Area of the Server
To completely secure a Web server, you would need to disconnect it from the network, unplug it, and bury it in a thick slab of concrete. This would guarantee its security by removing the possibility of any malicious interactions with the system. However, because this will also make the Web server useless, you have to find other ways to apply the security principle of reducing the attack surface area.
The ability of IIS 7.0 to remove virtually all features of the Web server is fundamental here, enabling us to eliminate the threat of any known or unknown attack vectors that may exist in those features. In addition, removing extra functionality reduces management complexity and reduces the chance of your server being forced offline if a patch affects the removed features. You can leverage this ability by doing the following:
Determine the set of features that you need for your server/application.
Install only the required IIS 7.0 features by using Windows Setup.
Manually remove any modules that your application does not need.
At the end of step 2, you should have the minimum required set of functionality installed globally for your server. You can then further reduce the surface area by disabling the modules you do not need in each of your applications, in the case where each application requires slightly different server functionality.
In some other cases, you may need to disable modules for the entire server if the setup packaging is not granular enough. This is often the case with ASP.NET, which installs all of the ASP.NET modules and handlers whether or not they are all necessary in your application.
Sounds simple, right? Unfortunately, the biggest challenge lies in step 1, determining the set of features your application needs. Doing this requires knowing which functionality can be safely removed without negatively affecting your application’s functionality or compromising its security. That’s right—you can actually end up making your server a lot less secure if you remove a security-sensitive feature. For example, if you remove an authorization module that is responsible for validating access to an administration page in your application, you may end up allowing anonymous users to take administrative action on your server! Likewise, removing certain features may contribute to decreased performance or reduced stability. Or removing these features may simply break your application.
Therefore, it is important to understand what features your application requires, and which it does not. To help with this, you can consult Table 12-9, which illustrates the function played by each built-in IIS 7.0 module whose removal may have a security impact on the server.
Table 12-9 Function of Built-In Modules with Security Implications
Module |
Purpose and Removal Effect |
Anonymous Authentication Module |
Purpose: Authenticates the request with an anonymous user if no other authentication mechanism is present. If removed: Access to resources will be denied for anonymous requests. |
Basic Authentication Module |
Purpose: Supports basic authentication. If removed: Clients will not be able to authenticate with basic authentication. |
Certificate Mapping Authentication Module |
Purpose: Supports client certificate authentication. If removed: Clients will not be able to authenticate with client certificates. |
Configuration Validation Module |
Purpose: Validates ASP.NET configuration in integrated mode If removed: Strong warning—ASP.NET applications that define modules and handlers using legacy configuration will silently run in integrated mode, but the modules will not be loaded. This may result in unexpected application behavior and security vulnerabilities for unmigrated ASP.NET applications. |
CustomError Module |
Purpose: Detailed error messages will not be generated for IIS errors. If removed: Strong warning—Sensitive application error information may be sent to remote clients. |
Default Authentication |
Purpose: Supports the ASP.NET DefaultAuthentication_OnAuthenticate event. When ASP.NET is configured to use the Forms Authentication mode, removing this module may lead to errors in other ASP.NET modules during anonymous requests. If removed: Warning—The DefaultAuthentication_OnAuthenticate event will not be raised, so any custom authentication code depending on this event will not run. This is not common. |
Digest Authentication Module |
Purpose: Supports digest authentication. If removed: Clients will not be able to use digest authentication to authenticate. |
File Authorization |
Purpose: Verifies that the authenticated client has access to physical resources. If removed: Strong warning—Access may be granted to resources that deny access to the authenticated user. |
Forms Authentication |
Purpose: Supports forms-based authentication. If removed: Clients will not be able to use forms authentication to authenticate. |
HttpCache Module |
Purpose: Supports IIS output caching and kernel caching of responses. If removed: Warning—Response output caching will not occur, possibly resulting in increased load on the server and in the worst case Denial of Service (DoS) conditions. |
HttpLogging Module |
Purpose: Supports request logging. If removed: Warning—Requests may not be logged. |
IISCertificate Mapping Authentication Module |
Purpose: Supports IIS configuration–based client certificate authentication. If removed: Clients may not be able to authenticate with client certificates against IIS configuration. |
HttpRedirection Module |
Purpose: Supports configuration-based redirection rules. If removed: Warning—If the application depends on redirection rules for restricting access to content, removing this module may make otherwise protected resources available. |
IsapiFilter Module |
Purpose: Supports ISAPI filters. If removed: Strong warning—ISAPI filters that enforce access or have other security functionality will not run. |
OutputCache |
Purpose: Supports ASP.NET response output caching. If removed: Warning—ASP.NET response output caching will not occur, possibly resulting in increased load on the server and in the worst case Denial of Service (DoS) conditions. |
RequestFiltering Module |
Purpose: Enforces various request restrictions and protects hidden content. If removed: Strong warning—Removing this module may result in protected content being served. It may also lead to nonspecific security vulnerabilities resulting from allowing unrestricted request input into the application. |
RoleManager |
Purpose: Supports the ASP.NET roles functionality. If removed: Strong warning—Roles for the authenticated user may not be available, which may cause authorization decisions to be affected. Typically, this will only restrict access, but in some cases where access is denied based on roles, this may grant access to otherwise unauthorized users. |
Static Compression Module |
Purpose: Supports compression of static resources. If removed: Warning—Removing this module may result in significantly higher bandwidth for the site, because compression of static resources will be disabled. |
Url Authorization |
Purpose: Supports declarative access rules. If removed: Strong warning—URL authorization access rules will be ignored, and access may be granted to unauthorized users. |
Url Authorization Module |
Purpose: Supports declarative ASP.NET access rules. If removed: Strong warning—ASP.NET url authorization access rules will be ignored, and access may be granted to unauthorized users. |
Windows Authentication |
Purpose: Supports NTLM and Kerberos authentication. If removed: Clients will be unable to authenticate with NTLM or Kerberos Windows authentication. |
Windows Authentication Module |
Purpose: Supports raising the ASP.NET WindowsAuthentication_OnAuthentication event. If removed: Warning—WindowsAuthentication_OnAuthentication event will not be raised, so any custom ASP.NET authentication code dependent on this event will not run. Note that this module is not required for Windows authentication. |
You should always verify that the application does indeed have all of the required modules enabled after deployment. In addition, you should always test the application whenever the module configuration changes to insure its correct operation with the new module set. Armed with the knowledge of which modules can be safely removed, you can take advantage of IIS 7.0’s modularity to significantly reduce the surface area of your server, without accidentally reducing its security.
Understanding and Reducing the Privilege of Code that Runs on Your Server
Now that you have reduced the surface area of your server to the acceptable minimum, you need to secure the remaining functionality. This is typically done in two ways: by restricting the inputs to the application as much as possible by using security features such as authorization and request filtering (IIS 7.0’s version of UrlScan) and by reducing the privilege with which the code in the application executes so that even if it is compromised, it is limited in the amount of harm it can do. You can learn more about both of these approaches in Chapter 14.
The former approach is an extension of reducing the surface area approach you took earlier, attempting to block as many of the attack vectors as possible by constraining the input to the server. The latter approach uses the principle of least privilege and focuses on what happens if the functionality on the server is compromised. With an understanding of how the extensibility code executes in the context of IIS, you can do much to reduce its privilege, which often makes compromising the server a lot harder or impossible. Also, this knowledge helps you understand the trust requirements for adding features or application components to the server.
Table 12-10 illustrates the privilege with which IIS Web server modules execute on the server.
Table 12-10 Module Privileges
Feature |
Execution Scope |
Privilege Level |
Who Can Add |
Native modules |
IIS worker process |
Application pool identity |
Administrator |
Managed modules and handlers |
ASP.NET appdomain |
Application pool identity (default) OR Authenticated user AND Limited by ASP.NET trust level |
Application owner |
ISAPI filters |
IIS worker process |
Application pool identity |
Administrator |
ISAPI extensions |
IIS worker process |
Authenticated user (default) OR Application pool identity |
Administrator |
CGI programs |
CGI program process (single-request) |
Authenticated user (default) OR Application pool identity |
Administrator |
FastCGI programs |
FastCGI program process |
Application pool identity |
Administrator |
The majority of IIS extensibility is hosted within a long-running IIS worker process (everything except CGI and FastCGI programs that execute out of process), which executes under the configured application pool identity (Network Service by default). This includes native modules as well as ISAPI extensions and filters (managed modules and handlers are also included, but they provide an additional constrained execution model that is discussed later in this chapter). This code, therefore, can do everything that the application pool identity is allowed to do, based on the privileges granted to the identity by the system and rights granted by ACLs on Windows resources. Reducing the privilege of this identity from Local System in IIS 5.1 to Network Service in IIS 6.0 was one of the fundamental security improvements that enabled IIS 6.0 to achieve its stellar security record. You can learn how to take advantage of reducing the privilege of the IIS application pools in Chapter 14.
Remember that despite all other constraining measures that may be in place, including ASP.NET Code Access Security, the privileges and rights granted to worker process that contains the code define what code in the process may or may not do (when impersonating, you also need to consider the rights and privileges assigned to the impersonated identity). In other words, when you add code to the server, even if it is application code, assume that it can do everything that your worker process is allowed to do. By maintaining least privilege application pools, you can significantly reduce the damage to the server in the case of an application compromise.
The story is slightly different when it comes to managed (ASP.NET) module and handler components. These components are hosted within the ASP.NET application, and in addition to being limited by the IIS worker process, they are also limited by the .NET Code Access Security (CAS) policy configured for the ASP.NET appdomain. This means that managed modules and handlers can execute with a lower privilege than the one granted by the IIS worker process identity.
By default, ASP.NET applications are configured to execute with Full trust, which means that they are not additionally constrained. By configuring the application to execute with lower trust levels via the system.web/trust configuration section, you can create an additional limitation on the execution of .NET code that precludes managed modules from performing certain actions and accessing resources outside of those located in their subdirectories. You can learn more about the built-in trust levels in Chapter 14.
The recommended trust level is Medium. At this trust level, the application cannot access resources that do not belong to it, though it can still use most ASP.NET features and execute code that affects its own operation. At this trust level, multiple applications running within a single application pool are largely isolated from each other, making this level the correct one to use for shared hosting (although it is preferable to host each customer in a separate fully isolated application pool), where hosted applications are allowed to upload code to the server.
You should take advantage of the Medium trust level where possible to further reduce the privilege of the managed components of your application. Be aware that some ASP.NET applications or modules may not function in Medium trust, due to the use of .NET APIs that required a higher trust level. The number of these applications is getting smaller, due to both API improvements in .NET Framework 2.0+ and application improvements to facilitate operation in partial trust environments. Also, the ASP.NET run time may experience reduced performance in a partial trust. This needs to be evaluated in each specific case to determine whether it is a significant enough factor to warrant using higher trust levels.
CGI and FastCGI programs are not hosted inside the IIS worker process, but instead execute inside their own processes that are spawned by the IIS worker process. CGI programs by default execute under the identity of the authenticated user, although you can configure them to execute with the identity of the worker process. Therefore, when using CGI programs, be aware that the code has the privilege of the invoking user. If that user is an administrator on the server, the code can perform administrative tasks that can lead to complete server compromise if the code is successfully exploited.
FastCGI programs always execute with the identity of the IIS worker process, so they have the same privilege as code in the IIS worker process. FastCGI does provide a way for the FastCGI program to impersonate the authenticated user, which can be done by PHP in FastCGI mode. In this case, the same warning applies to code running in the worker process as when running CGI programs.
All that said, the bottom line is that you must trust the source of the code running on your server as far as the privilege level under which this code executes. If you do not trust the code, you must insure that it runs with a low privilege level by constraining it with a very low privilege application pool identity. If the code is native, that is the best you can do. If the code is managed, you can use ASP.NET’s partial trust levels to further constrain it, which provides a foundation for allowing third-party application code to run on your server.
If you do trust the code, you can harden it against unforeseen exploits by reducing its privilege as much as possible using the techniques described earlier in this chapter. Though you can never be completely sure that a piece of code is foolproof against attacks, using the least privilege principle can significantly limit or eliminate damages.
Locking Down Extensibility
Now that you have built a minimal surface area Web server that runs with least privilege, you need to make sure it stays that way. If you are running a dedicated server, this is less of an issue because you are the one who controls the configuration that defines which components are enabled and how they execute. However, if you delegate application management to someone else, as is the case with shared hosting servers and sometimes departmental servers, things are different.
To understand this, let’s first look at the difference in extensibility delegation between IIS 6.0 and IIS 7.0. In IIS 6.0, the administrator in the metabase controls the server functionality. If a user wants to serve .php3 pages with the PHP scripting engine, they need to contact the administrator to create a handler mapping for their application. The same is the case for adding or removing an ISAPI filter. In IIS 7.0, the delegated configuration system enables applications to remove or add new functionality in some cases, without requiring administrator level changes. This is nice for allowing xcopy deployment of applications that define their own configuration and functionality, and reducing total cost of ownership. However, in some cases, this may be undesired from a security perspective, and so the administrator has a fine degree of control over what changes are allowed at the application level. This is done by controlling configuration delegation for the system.webServer/handlers and system.webServer/modules configuration sections via configuration locking.
In the default IIS 7.0 installation, both of these sections are locked at the server level. This means that application A cannot add new modules or create new handler mappings, and application B cannot remove or change existing modules or handler mappings.
This is a very restrictive state that prevents many ASP.NET applications from working correctly, because ASP.NET applications often need to declare new modules and create new handler mappings. In general, it prevents IIS applications from defining their handler mappings and modules in their configuration. Because of this, when the “.NET Extensibility” or the “ASP.NET” role service is installed on the server, these sections are unlocked. This allows applications to specify a configuration that does the following:
Enable/add new managed modules.
Disable/remove existing modules.
Add new handler mappings.
Override/remove existing handler mappings.
Because adding new native modules requires installing them at the server level, and adding new ISAPI filters/extensions and CGI/FastCGI programs also requires configuration changes at the server level (the isapiCgiRestrictions and fastCgi sections), applications cannot introduce new native code. However, they can introduce new managed modules and handlers. Because of this, in shared environments where the application is not trusted by the administrator, server administrators must do one of the following:
Prevent new managed modules/handlers from being added by locking the modules and handlers sections. This will break many applications (especially ASP.NET applications running in Integrated mode).
Reduce the trust level of the application to Medium trust to constrain the execution of managed code.
Use a low-privilege application pool to host the application.
The application can also by default disable any of the modules defined at the server level. This can be a problem if the server administrator wants to mandate the presence of a particular module, such as a bandwidth monitor module or logging module that tracks the operation of the application. To counteract that, the server administrator can lock each module that should not be removable at the server level, preventing it from being removed by the application. This can be done by adding a lockItem = “true” attribute to each module element in the modules configuration section at the server level. In fact, when the modules section is unlocked, ASP.NET setup automatically locks each native module that is installed against removal (in some cases, you may want to unlock some of these modules if you do not mind them being disabled by the application).
Because the application can also create new handler mappings, the application can override mappings defined at the server level. The locking approach does not work here because new mappings take precedence over earlier mappings, so it is not necessary to remove existing mappings to redirect them to other handler types. However, the ability to remap requests in the application to another handler is not a security concern outside of the ability to execute the code, which is already controlled by the application trust level and/or the application pool identity. The handlers section does expose the accessPolicy attribute, which controls what access levels are granted to the handlers. This attribute is by default locked at the server level, so the application cannot modify it.
The trust level configuration for the application is also something that should be locked at the server level. By default, it isn’t—so the application can elevate its own trust level to Full to remove the constraint on the execution of the .NET code. Server administrators should always lock this configuration in the framework’s root Web.config file to prevent it from being overridden when they are relying on the partial trust security model for applications. For convenience, you can do this using the following Appcmd command.
AppCmd lock config /section:trust /commit:MACHINE/WEBROOT
This prevents applications from overriding the trust level setting in the framework root Web.config.