(待翻译)Building Secure ASP.NET Applications: Authentication, Authorization, and Secure Communication
IIS and ASP.NET Processing
Note The information in this section applies to Internet Information Services (IIS) 5, running on Windows® 2000.
ASP.NET Web applications and Web services are processed by code that executes in a single instance of the ASP.NET worker process (aspnet_wp.exe), although on multi-processor computers, you can configure multiple instances, one per processor.
IIS authenticates callers and creates a Windows access token for the caller. If anonymous access is enabled within IIS, then a Windows access token for the anonymous Internet user account (typically, IUSR_MACHINE) is created by IIS.
Requests for ASP.NET file types are handled by an ASP.NET ISAPI extension (aspnet_isapi.dll), which runs in the IIS (inetinfo.exe) process address space. This uses a named pipe to communicate with the ASP.NET worker process as shown in Figure 1. IIS passes the Windows access token that represents the caller to the ASP.NET worker process. The ASP.NET Windows authentication module uses this to construct a WindowsPrincipal object and the ASP.NET File authorization module uses it to perform Windows access checks to ensure the caller is authorized to access the requested file.
Figure 1. IIS and ASP.NET communication
Note Access tokens are process relative. As a result, the ASP.NET ISAPI DLL running in inetinfo.exe calls DuplicateHandle to duplicate the token handle into the aspnet_wp.exe process address space and then passes the handle value through the named pipe.
Separate application domains within the worker process (one per IIS virtual directory, or in other words, one per ASP.NET Web application or Web service) are used to provide isolation.
This is in contrast to classic ASP, where the application protection level, configured within the IIS metabase determined whether the ASP application should execute in process with IIS (inetinfo.exe), out of process in a dedicated instance of Dllhost.exe, or in a shared (pooled) instance of Dllhost.exe.
Important The process isolation level setting within IIS has no affect on the way ASP.NET Web applications are processed.
The ASP.NET ISAPI Extension
The ASP.NET ISAPI extension (aspnet_isapi.dll) runs in the IIS process address space (inetinfo.exe) and forwards requests for ASP.NET file types to the ASP.NET worker process through a named pipe.
Specific ASP.NET file types are mapped to the ASP.NET ISAPI extension by mappings defined within the IIS metabase. Mappings for standard ASP.NET file types (including .aspx, .asmx, .rem, .soap) are established when the .NET Framework is installed.
To view application mappings
- From the Administrative Tools programs group, start Internet Information Services.
- Right-click the default Web site on your Web server computer, and then click Properties.
- Click the Home Directory tab, and then click Configuration.
A list of mappings is displayed. You can see which file types are mapped to Aspnet_isapi.dll.
IIS 6.0 and Windows Server
IIS 6.0 on Windows Server will introduce some significant changes to the current process arrangement.
- You will be able to configure multiple application pools, each served by one or more process instances (w3wp.exe). This will provide additional fault tolerance and manageability benefits and will allow you to isolate separate applications in separate processes.
- ASP.NET is integrated with the IIS 6.0 Kernel mode HTTP listener, which will allow requests to be passed directly from the operating system to the ASP.NET worker process.
ASP.NET authentication and authorization mechanisms are implemented using HTTP module objects, which are invoked as part of the standard ASP.NET pipeline processing. Individual Web requests and responses pass through a pipeline of objects as shown in Figure 2.
Figure 2. ASP.NET pipeline processing
The ASP.NET pipeline model consists of an HttpApplication object, various HTTP module objects, and an HTTP handler object, together with their associated factory objects, which have been omitted from Figure 2 for clarity. An HttpRuntime object is used at the start of the processing sequence and an HttpContext object is used throughout the lifecycle of a request to convey details about the request and response.
The following list explains the responsibilities and operations performed by the objects associated with the HTTP processing pipeline:
- The HttpRuntime object examines the request received from IIS and dispatches it to an appropriate instance of the HttpApplication object to process the request. There is a pool of HttpApplication objects
in each application domain in Aspnet_wp.exe. There is a one-to-one mapping between application domains, HttpApplication objects and IIS virtual directories. In other words, ASP.NET treats separate IIS virtual directories as separate applications.
Note There is one instance of HttpRuntime in every Web application domain.
- The HttpApplication objects control the pipeline processing. An individual HttpApplication object is created to handle each simultaneous HTTP request. HttpApplication objects are pooled for performance reasons.
- HTTP module objects are filters that process HTTP request and response messages as they flow through the pipeline. They can view or alter the content of the request and response messages. HTTP modules are classes that implement IHttpModule.
- HTTP handler objects are the endpoints for HTTP requests and provide the request processing for specific file types. For example, one handler processes requests for *.aspx files while another processes requests for *.asmx files. The HTTP response message is generated and returned from the HTTP handler. HTTP handlers are classes that implement IHttpHandler.
- An HttpContext object is used throughout the pipeline to represent the current Web request and response. It is available to all modules in the pipeline and the handler object at the end of the pipeline. The HttpContext object exposes various properties including the User property which contains an IPrincipal object that represents the caller.
The Anatomy of a Web Request
The ASP.NET ISAPI library (Aspnet_isapi.dll) runs inside the IIS process address space (Inetinfo.exe). It dispatches requests to the HttpRuntime object within the ASP.NET worker process (Aspnet_wp.exe). The following set of actions occurs in response to each Web request received by ASP.NET:
- The HttpRuntime object examines the request and forwards it to an instance of an HttpApplication object.
There is at least one HttpApplication object instance per application domain (the objects are pooled) and one application domain per IIS virtual directory. The initial request for a file in a particular virtual directory results in a new application domain and a new HttpApplication object being created.
- A list of HTTP modules is read from Machine.config (they are contained within the <httpModules> element). Additional custom HTTP modules can be added to Web.config for a specific application. The default <httpModules> element
within Machine.config is shown in the following code snippet.
<httpModules> <add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/> <add name="Session" type="System.Web.SessionState.SessionStateModule"/> <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule"/> <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule"/> <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule"/> <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule"/> <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule"/> </httpModules>
The authentication modules hook the AuthenticateRequest event, while the authorization modules hook the AuthorizeRequest event.
The request passes through every module in the pipeline, although only a single authentication module is loaded. This depends on the configuration of the <authentication> element in Web.config. For example, the <authentication> element that follows results in theWindowsAuthenticationModule being loaded.
<authentication mode="Windows" />
- The activated authentication module is responsible for creating an IPrincipal object and storing it in the HttpContext.User property. This is vital, because the downstream authorization modules use this IPrincipal object
in order to make authorization decisions.
In the absence of authentication (for example, where anonymous access is enabled within IIS and ASP.NET is configured with <authentication mode="None" />), there's a special non configured module that puts a default anonymous principal into the HttpContext. User property. As a result, HttpContext.User is always non-null after authentication.
If you implement a custom authentication module, code within the custom module must create an IPrincipal object and store it inHttpContext.User,
Note ASP.NET also wires up Thread.CurrentPrincipal based on HttpContext.User after the AuthenticateRequest event.
- The HttpApplication fires the AuthenticateRequest event, which can be hooked in global.asax. This allows you to inject custom processing code; for example, to load the set of roles associated with the current user. However, note that the WindowsAuthenticationModule does this automatically. The role list is obtained from the set of Windows groups in which the authenticated Windows user is a member.
- After the appropriate authentication module has finished its processing, the authorization modules are called if the request hasn't been aborted.
- When the UrlAuthorizationModule is called, it checks for an <authorization> tag in Machine.config and Web.config. If present, it retrieves theIPrincipal object from HttpContext.User and
checks to see whether the user is authorized to access the requested resource using the specified verb (GET, POST, and so on).
If the user is not authorized, the UrlAuthorizationModule calls HttpApplication.CompleteRequest, which aborts normal message processing. The UrlAuthorizationModule returns an HTTP 401 status code.
- Next, the FileAuthorizationModule is called. It checks whether the IIdentity object in HttpContext.User.Identity is an instance of theWindowsIdentity class.
If the IIdentity object is not a WindowsIdentity, the FileAuthorizationModule performs no further processing.
If a WindowsIdentity is present, the FileAuthorizationModule calls the AccessCheck API (through P/Invoke) to see if the authenticated caller (whose access token has been passed to ASP.NET by IIS and is exposed by the WindowsIdentity object) is authorized to access the requested file. If the file's security descriptor contains at least a Read ACE in its DACL, the request is allowed to proceed. Otherwise theFileAuthorizationModule calls HttpApplication.CompleteRequest and returns a 401 status code.
Forms authentication processing
The FormsAuthenticationModule is activated when the following element is in Web.config.
<authentication mode="Forms" />
Remember that for Forms authentication, you implement the Application_Authenticate event in Global.asax. For Forms authentication, the following sequence occurs:
- Within this code, you can construct an IPrincipal object and store it in HttpContext.User. This typically contains the role list retrieved from a custom data store (normally a SQL Server database or Active Directory). The IPrincipal object
is typically an instance of the GenericPrincipalclass but could also be a custom IPrincipal class.
The FormsAuthenticationModule checks to see if you have created an IPrincipal object. If you have, it is used by the downstream authorization modules. If you haven't, the FormsAuthenticationModule constructs a GenericPrincipal (with no roles) and stores it in the context.
If there is no role information, any authorization checks (such as PrincipalPermssion demands) that demand role membership, will fail.
- The UrlAuthorizationModule handles the AuthorizeRequest event. Its authorization decisions are based on the IPrincipal object contained within HttpContext.User.
Windows authentication processing
The WindowsAuthenticationModule is activated when the following element is in Web.config.
<authentication mode="Windows" />
For Windows authentication, the following sequence occurs:
- The WindowsAuthenticationModule creates a WindowsPrincipal object using the Windows access token passed to ASP.NET by IIS.
- It uses P/Invoke to call Win32 functions to obtain the list of Windows group that the user belongs to. These are used to populate theWindowsPrincipal role list.
- It stores the WindowsPrincipal object in HttpContext.User, ready to be used by the downstream authorization modules.
The HttpApplication object fires the set of events shown in Table 1. Individual HTTP modules can hook these events (by providing their own event handlers).
Table 1. Events fired by HttpApplication objects
|BeginRequest||Fired before request processing starts|
|AuthenticateRequest||To authenticate the caller|
|AuthorizeRequest||To perform access checks|
|ResolveRequestCache||To get a response from the cache|
|AcquireRequestState||To load session state|
|PreRequestHandlerExecute||Fired immediately before the request is sent to the handler object|
|PostRequestHandlerExecute||Fired immediately after the request is sent to the handler object|
|ReleaseRequestState||To store session state|
|UpdateRequestCache||To update the response cache|
|EndRequest||Fired after processing ends|
|PreSendRequestHeaders||Fired before buffered response headers are sent|
|PreSendRequestContent||Fired before buffered response body sent|
Note The HTTP handler executes in between the PreRequestHandlerExecute and PostRequestHandlerExecute events.
The last two events are non-deterministic and could occur at any time (for example, as a result of a Response.Flush). All other events are sequential.
You do not need to implement an HTTP module simply in order to hook one of these events. You can also add event handlers to Global.asax. In addition to the events listed in Table 1 (which can all be hooked by individual HTTP module objects), the HttpApplication object firesApplication_OnStart and Application_OnEnd handlers, which will be familiar to ASP developers. These can be handled only within Global.asax. Finally, you can also implement custom event handlers within Global.asax for events fired by individual HTTP module objects. For example, the session state module fires Session_OnStart and Session_OnEnd events.
Implementing a Custom HTTP Module
To create your own HTTP module and insert it into the ASP.NET processing pipeline
- Create a class that implements IHttpModule.
- Place the assembly that contains the module in your application's \bin subdirectory or you can install it into the Global Assembly Cache.
- Add an <HttpModules> element to your application's web.config, as shown below.
<system.web> <httpModules> <add name="modulename" type="namespace. classname, assemblyname" /> </httpModules> </system.web>
Implementing a Custom HTTP Handler
You may need to implement a custom HTTP handler, for example to handle the processing of files with the .data file extension.
To implement a custom HTTP handler
- Add a mapping to the IIS metabase to map the .data file extension to the ASP.NET ISAPI extension (Aspnet_isapi.dll).
Right-click your application's virtual directory in the IIS MMC snap-in, click the Configuration button, and then click Add to create a new mapping for .data files to C:\Winnt\Microsoft.NET\Framework\v1.0.3705\aspnet_isapi.dll.
Note If you select the Check that file exists check box when adding the mapping, then the file must be physically present. This is usually what is wanted unless you have virtualized paths that don't map to a physical file. Virtualized paths ending with .rem or .soap are used by .NET Remoting.
- Create a class that implements IHttpHandler (and optionally IHttpAsyncHandler if you want to handle requests asynchronously).
- Place the assembly that contains the handler in your application's \bin subdirectory or you can install it into the Global Assembly Cache.
- Add the handler to the processing pipeline by adding an <httpHandlers> section to your application's Web.config file.
<system.web> <httpHandlers> <add verb="*" path="*.data" type="namespace.classname, assemblyname" /> </httpHandlers> </system.web>