Share via


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.

Note In IIS 7.0, the amount of processing the Web server engine itself performs is minimal, with the modules providing most of the request processing.

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.

Dd163535.figure_C12624412_2(en-us,TechNet.10).png

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

Dd163535.table_C12624412_1(en-us,TechNet.10).png

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

Dd163535.table_C12624412_2(en-us,TechNet.10).png

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.

How It Works: ASP.NET Integrated Pipeline

With the unified pipeline model that IIS 7.0 provides, both native modules developed using the IIS 7.0 native extensibility model and managed modules developed using the ASP.NET module model can participate in the Web server's request processing (when using the ASP.NET Integrated mode). Both native and managed modules can participate in all request processing stages and operate on a shared set of request and response intrinsic objects.

In practice, however, ASP.NET and IIS are two separate software products. Moreover, ASP.NET Integrated mode uses the standard ASP.NET interfaces that are used to provide request processing services to the ASP.NET application framework on previous versions of IIS. How, then, is such a tight integration possible?

The answer lies in the special native module, ManagedEngine, that is installed on IIS 7.0 when the ".NET Extensibility" Windows Setup component (Windows Vista) or Role Service (Windows Server 2008) is installed. This module implements the ASP.NET Integrated mode engine that enables the ASP.NET request processing pipeline to be overlaid on the IIS request processing pipeline, proxying the event notifications and propagating the required request state to support the pipeline integration. This module is responsible for reading the managed modules and handler entries in the IIS module and handler configuration and working together with the new ASP.NET engine implementation in System.Web.dll to set up the integrated pipeline. As a result, it enables ASP.NET modules and handlers to act as IIS modules and handlers.

So, when you see the ManagedEngine module in the IIS modules list, pay it some respect—it is arguably the most complex and powerful module ever written for IIS 7.0. Also keep in mind that this module must be present for the integrated pipeline and ASP.NET applications in general to work in IIS 7.0 Integrated mode application pools.

Mike Volodarsky

IIS Core Program Manager

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.

Note Native module DLLs should be located on the server's local file system and not on remote network shares. This is because the server attempts to load them under the application pool identity and not the identity of the authenticated user or the configured virtual path (UNC) identity. This identity will not typically have access to network shares.

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.

Note By adding the module to globalModules, you are instructing IIS worker processes to load the module DLL. This alone does not enable the module to run. To do that, you also need to enable the module on the server or for a particular application.

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.

Note IIS 7.0 does provide a way to load native modules selectively into a specific application pool, by using the application pool name preconditions. See the section titled "Understanding Module Preconditions" later in this chapter for more information on this. Though loading native modules in this way is possible, you should not use this mechanism in most situations because of its management complexity.

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.

Inside Global Web Server Events

If you read the globalModules section carefully, you will notice that some modules, such as the TokenCacheModule, are listed there but yet are not listed in the modules list by default. Does this mean that this module is disabled by default? No, not entirely.

Native modules loaded inside the IIS worker process can participate in global server events, which are events that are not associated with request processing. These events enable native modules to extend certain server functionality at the worker process level, such as by providing the ability to cache logon tokens for improved performance.

Native modules that offer this kind of global functionality do not need to be listed in the modules list and are able to offer it by simply being loaded in the worker process. However, only modules listed in the modules list can provide request processing functionality.

Note When the modules section changes, the IIS worker process does not need to recycle. Instead, it picks up the changes automatically and applies the resulting module set to subsequent requests. However, ASP.NET applications whose modules configuration changes will restart.

Uninstalling Native Modules

Caution Before removing modules, you should consider the security and performance implications that the module removal will have on your server. The section titled "Securing Web Server Modules" later in this chapter covers these implications in more detail.

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 global-Modules 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.

Caution You should not remove built-in IIS 7.0 modules manually. Use Windows Setup instead to uninstall the corresponding feature or role service.

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.

Note Running managed modules requires installation of the ".NET Extensibility" Windows Setup component (Windows Vista) or Role Service (Windows Server 2008). This installs the ManagedEngine module that enables managed modules to run inside Integrated mode applications pools.

Installing the "ASP.NET" setup component/role service automatically installs the ".NET Extensibility" component and also adds the modules and handler mappings used by the ASP.NET framework. It also installs the classic ASP.NET handler mappings that enable application pools that use Classic integration mode to run ASP.NET using the legacy ASPNET_ISAPI.dll integration mechanism.

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).

Important ASP.NET applications that define modules in the system.web/httpModules configuration section and ASP.NET handler mappings in the system.web/httpHandlers configuration section need to have their configurations migrated to the IIS system.webServer/modules and system.webServer/handlers configuration sections to operate correctly in Integrated mode. The server will generate a HTTP 500 error notifying you of this requirement if you attempt to run such an application in Integrated mode. You can migrate the application easily by using the Appcmd Migrate Config ApplicationPath command. To learn more about why this is necessary and the options you have for running legacy ASP.NET applications, see Chapter 11, "Hosting Application Development Frameworks."

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

Dd163535.table_C12624412_3(en-us,TechNet.10).png

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 https://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

Note The gacutil.exe command line tool is not available in the .NET Framework run-time installation that comes with the operating system, so you have to download the .NET Framework SDK to obtain it. After you obtain it, though, you can copy the tool to use on other machines.

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

Note You can get the assembly part of the strong name by using the gacutil.exe tool you used earlier when you installed the assembly to the Global Assembly Cache. Run "gacutil.exe /l AssemblyName" to display the assembly's strong name signature. You can omit all parts of the assembly's strong name signature except for the assembly name, and ASP.NET will attempt to find the first matching assembly based on the attributes you do include.

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.

Note It is not necessary to sign the assembly in the application's /BIN subdirectory.

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

Caution Before removing modules, you should consider the security and performance implications that this action will have on your server. You can find more detail in the section titled "Securing Web Server Modules" later in this chapter.

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

Dd163535.table_C12624412_4(en-us,TechNet.10).png

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.

Note You can manage the enabled modules for your application by using the IIS Manager. After selecting your application in the tree view and opening the Modules feature, use the Add Managed Module action to add a new managed module, the Configure Native Modules action to enable or disable existing native modules, or the Edit or Remove actions to edit or remove existing module entries in the list. See the section titled "Using IIS Manager to Install and Manage Modules" later in this chapter for more information.

Note You can also use the Appcmd command line tool to manage the enabled modules. See the section titled "Using Appcmd to Install and Manage Modules" later in this chapter for more information.

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.

Note New managed modules you add will not have the managedHandler precondition by default and will run for all requests. If you want to restrict the managed module to run only for requests to managed handlers, you need to manually add the managedHandler precondition.

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>

Note You can also use IIS Manager to perform the ordering task. After selecting your application in the tree view and opening the Modules feature, choose the View Ordered List action and use the Move Up and Move Down actions to adjust the sequence. If you use this feature, the tool will use the approach that we discussed earlier to reorder the -modules for your application.

Caution By using the approach, you are effectively disconnecting the application's module configuration from the configuration at the server level. Therefore, any changes made at the server level (removing or adding modules) will no longer affect the application and will need to be manually propagated if necessary.

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

Dd163535.table_C12624412_5(en-us,TechNet.10).png

The information in the handler mapping is used as follows.

  1. 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.
  2. 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.
  3. If the accessPolicy configuration does not meet the requireAccess requirement for the handler mapping, a "403 Access Denied" error is generated.
  4. 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. Note The path attribute in IIS 7.0 enables you to specify more complex path masks to match the request URL than previous versions of IIS, which enable only * or .ext where ext is the URL extension. IIS 7.0 enables you to use a path mask that may contain multiple URL segments separated by / and to use wildcard characters such as * or ?.

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.

Note IIS 7.0 continues to support wildcard mappings, which enable a handler to act like a filter, processing all requests and possibly delegating request processing to another handler by making a child request. Though the majority of such scenarios can now be implemented with normal modules, quite a few legacy ISAPI extensions take advantage of this model (including ASP.NET in some configurations). To create a wildcard mapping, you need to set the path and verb attributes to *, set the requireAccess attribute to None, and set the resourceType attribute to Either.

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

Dd163535.table_C12624412_6(en-us,TechNet.10).png

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.

Security Alert

The fact that the catch-all mapping uses StaticFileModule means that requests to resources that have not yet had a handler configured but are not listed in the server's MIME type configuration will result in a "404.3 Not Found" error. This error typically indicates that either you need to add a MIME map entry for the file's extension to the server's staticContent configuration section to allow the file to be downloaded, or you need to add a handler mapping to appropriately process the file. This is an important security measure that prevents scripts from being downloaded as source code on servers that do not yet have the right handler mappings installed. For more information on adding MIME type entries, see Chapter 11.

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.

Dd163535.figure_C12624412_3(en-us,TechNet.10).png

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.

    Dd163535.figure_C12624412_3A(en-us,TechNet.10).png

    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.

    Dd163535.figure_C12624412_3B(en-us,TechNet.10).png

    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:

  1. 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.
  2. 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.
  3. 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.

Dd163535.figure_C12624412_4(en-us,TechNet.10).png

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.

    Dd163535.figure_C12624412_5(en-us,TechNet.10).png

    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.

    Dd163535.figure_C12624412_6(en-us,TechNet.10).png

    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.

    Dd163535.figure_C12624412_7(en-us,TechNet.10).png

    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.

Note It is not possible to edit preconditions via the Handler Mappings feature. Instead, the tool automatically generates the correct preconditions for handler mappings to make it function correctly by default. If you would like to change the preconditions, you need to edit configuration directly by hand, with Appcmd, or with other configuration APIs.

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 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

Dd163535.table_C12624412_7(en-us,TechNet.10).png

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.

Tip The uninstall command uses the standard Appcmd identifier pattern for specifying the module name, as opposed to using the /name parameter that the Install command uses. This is done so that all commands that work on specific existing instances of objects can use the identifier format instead of using different parameter names to identify objects.

Here is an example of how you can uninstall the module you installed earlier.

AppCmd uninstall module MyNativeModule

Note This also automatically disables the module at the server level, which makes sense because allowing this module to be enabled would result in an incorrect configuration after the module is uninstalled. However, if for some reason you wanted to uninstall a module but leave it enabled (this would cause request errors on the entire server if left unchanged), you can specify the /remove:false parameter.

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

Dd163535.table_C12624412_8(en-us,TechNet.10).png

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]

Tip The delete command uses the standard Appcmd identifier pattern for specifying the module name, as opposed to using the /name parameter that the add command uses. This syntax exists so that all commands that work on specific existing instances of objects can use the identifier format instead of using different parameter names to identify objects.

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.

Note Because applications can remove certain modules or add new ones, their module set can often be different from that of the server itself. Be sure to check the module list for the application you're interested in when you're investigating problems with that application.

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']"

Note Long commands are sometimes shown formatted on multiple lines to fit on the printed page.

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/isapiCgi-Restriction 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.

Note FastCGI program executables are not added to the isapiCgiRestriction list. Instead, they must be registered in the system.webServer/fastCGI configuration section to allow FastCGI application pools to be created for this executable.

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.

Direct from the Source: The Most Secure Web Server in the World

My first demo for the IIS 7.0 breakout session at the conference TechEd 2005 was to showcase the componentization capabilities of IIS 7.0 by showing off the "most secure Web server in the world."As part of the demo, I showed editing the configuration in the ApplicationHost.config file, removing all of the modules, and removing handler mappings. After saving the file, the IIS worker process automatically picked up the changes and restarted, loading absolutely no modules. After making a request to the default Web site, I got back a swift empty 200 response (this configuration currently returns a "401 Unauthorized" error because no authentication modules are present). The server performed virtually no -processing of the request and returned no content, thus becoming the "most secure Web server in the world." After a pause, I commented that though secure, the server was also fairly useless. Then I segued into adding back the functionality that we needed for our specific application.I have done this demo before for internal audiences to much acclaim, but I will always remember the audience reaction during the TechEd presentation. The people in the audience went wild, some breaking out into a standing ovation. This was a resounding confirmation of our efforts to give administrators the ability to start from nothing, building up the server with an absolutely minimal set of features to produce the most simple-to-manage, low-surface-area Web server possible.Mike VolodarskyProgram Manager, IIS 7.0

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:

  1. Determine the set of features that you need for your server/application.
  2. Install only the required IIS 7.0 features by using Windows Setup.
  3. 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.

Caution It is possible to have IIS configuration that configures an IIS feature to perform a certain function and yet to not have this function be performed by the server if the module that is responsible for this function is not enabled. This can happen if someone disables the module or configures its preconditions to prevent it from running in a specific application pool. Because of this, you need to make sure that the required modules are present and are correctly preconditioned to insure the correct operation of 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

Dd163535.table_C12624412_9(en-us,TechNet.10).png

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

Dd163535.table_C12624412_10(en-us,TechNet.10).png

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.

Note You can also use IIS Manager to configure the default trust level for ASP.NET applications or the trust level for a particular application on your server.

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.

Note Though ASP.NET trust levels provide an additional way to constrained the execution of managed code on your server, they should not be used as a substitute for reducing the privilege of the hosting IIS worker process.

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.

Note When using anonymous authentication while impersonating, the new IIS_IUSR account will be used to execute the CGI, FastCGI, and ISAPI extension–based applications (unless the server is configured to use the process identity instead). Unless you are using the IIS_IUSR account to use lower privilege for the executing code than the one provided by the worker process identity, you should consider using the worker process identity instead to manage one less account. You can do this by setting the userName attribute of the anonymousAuthentication section to "".

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.

Important In a number of cases, server code impersonates the authenticated user. This is done by default for all ISAPI extensions, ASP pages, PHP pages running in CGI, ISAPI or FastCGI mode, and ASP.NET applications that enable impersonation. If impersonation is used, be aware that the code will execute with very high privileges when the request is made by a user who has administrative privileges on this machine. You should strongly consider disallowing administrative users from using your application except when necessary.

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:

  1. Enable/add new managed modules.
  2. Disable/remove existing modules.
  3. Add new handler mappings.
  4. 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.

On the Disc Browse the CD for additional tools and resources.

< Back      Next >

 

 

© Microsoft. All Rights Reserved.