ASP.NET Web Services Security

Security is an important issue for many Web sites, whether they are serving Web pages or running Web services. This is especially true if the Web site is exposed on the Internet. The site might contain sensitive data that, in the wrong hands, could cause substantial damage to the company or the individuals. Such a site may wish to allow only specific users to be able to access the site. The site may wish to authenticate the user; that is, verify the user is the one he or she claims to be. The site might want to implement some kind of authorization such as role-based security so that users get restricted access to the data, based on their privileges. Finally, under some circumstances, the executing code might wish to impersonate the user so that certain secured objects at the OS level can be accessed.

In this section we examine how ASP.NET provides support for handling these security issues. Although our focus will be from the Web services perspective, the same principles are applicable if you are serving Web pages using any server-side code.

Before we go further, it is important to understand how ASP.NET applications are hosted under IIS and the security context under which the ASP.NET applications run.

Hosting ASP.NET under IIS

Figure 9.7 illustrates how ASP.NET applications are executed under IIS.

Figure 9.7. Hosting ASP.NET Applications under IIS.


Under IIS, any HTTP request to ASP.NET files such as .asmx, .aspx, and so on, is handled by an ISAPI DLL called aspnet_isapi.dll. This ISAPI DLL always runs inside the core IIS server process, InetInfo.exe. The DLL doesn't do much except to forward incoming requests to an ASP.NET worker process called aspnet_wp.exe over a named pipe. The ASP.NET worker process is automatically created if it doesn't exist.

The ASP.NET worker process hosts your ASP.NET code. It compiles your server-side pages as need be and executes your code, loading whatever assemblies are necessary.

No matter how many ASP.NET applications you are running on your system, all are hosted in just one ASP.NET worker process. To provide isolation between the applications, each application is run under a separate AppDomain.

ASP.NET Changes in IIS 6.0

IIS 6.0 is currently slated to be released along with Windows .NET Server. Under IIS 6.0, aspnet_wp.exe does not exist. Instead, IIS provides the worker processes for ASP.NET as well as other applications. At the time of this writing, Microsoft plans to connect Inetinfo.exe to the worker processes using local procedure calls (LPCs) instead of named pipes.


By default, aspnet_wp.exe runs under an account name ASPNET. This is a low-privileged user account that is created at the time of .NET Framework installation.

Although running the ASP.NET worker process under a low-privileged account is desirable from a security perspective, it is possible to configure aspnet_wp.exe to run under a different account. This is done by modifying the <processModel> XML element in the global configuration file, Machine.config. This element is used to define various process settings by means of XML attributes. These attributes are well-documented in the SDK. A limited explanation of these attributes is also available in the configuration file.

The two attributes that are important to us are username and password. The default settings for these two attributes are:

userName="machine" password="AutoGenerate"

This setting causes aspnet_wp.exe to run under the ASPNET account.

To run aspnet_wp.exe under the local SYSTEM account, which is a high-privileged administrator account, you can define the settings as follows:

userName="SYSTEM" password="AutoGenerate"

User names machine and SYSTEM are special tokens that ASP.NET is aware of. The password value of AutoGenerate can be applied only to these two accounts, eliminating the need to enter the actual passwords.

To run aspnet_wp.exe under a specific account, you can specify an explicit user name and password, as shown here:

userName="jay" password="jayspassword"

Be aware that Machine.config is readable by anyone who has physical access to the machine. You might have to take extra precautions to secure access to the machine.

This concludes the short introduction to how ASP.NET applications are hosted and executed. Interested readers can read [Bro-01b] for more details.

Authentication

Authentication is the process of accepting credentials from a user and validating those credentials against a designated authority. The client must provide credentials to allow the server to verify the client's identity.

In the case of ASP.NET, the clients have to go through two different authentication checks. The first-level authentication is done by IIS and the second-level authentication is done by ASP.NET.

IIS Authentication

IIS offers four basic modes of authentication:

  1. Anonymous access

  2. Integrated Windows authentication

  3. Basic authentication

  4. Digest authentication

An IIS virtual directory can be set to use one or more of these authentication modes. A snapshot of the Authentication Methods dialog box is shown in Figure 9.8.

Figure 9.8. Authentication Methods under IIS.


The client code has to provide proper credentials to IIS to clear the authentication check. Let's see how this can be done in the client code for each of the four modes.

For our experiment, we use the following Web service class (creating Web services was discussed in Chapter 6):

// Project WebSecurity. File MyTest.asmx

<%@ WebService Language="C#" Class="MyCompany.MyTest" %>

[WebService(Namespace="http://localhost/Demo/")]
public class MyTest : WebService {

     [WebMethod]
     public String WhoAmI() {
       return Thread.CurrentPrincipal.Identity.Name;
     }
}

The Web service class MyTest implements a method WhoAmI that simply returns the name of the identity associated with the managed thread on which the method is called.

For the experiment, the Web service is hosted on the local machine under the IIS virtual directory Demo. This allows the Web service to be accessed as http://localhost/Demo/MyTest.asmx.

Recall that it is the responsibility of the client to provide the credentials to the server. Under .NET, the client's credentials are represented in a standard interface ICredentials. The framework also comes standard with two classes based on this interface, NetworkCredential and CredentialCache. The NetworkCredential class provides credentials for password-based authentication schemes such as basic, digest, NTLM, and Kerberos. The CredentialCache provides storage for multiple credentials. We use these classes later in our examples.

Note that the types ICrendentials, NetworkCredential, and CredentialCache are defined under the namespace System.Net, which provides a simple programming interface for many of the common network protocols.

To consume the Web service, our client application generates and uses a proxy class MyTest using wsdl.exe (Chapter 6). The client code creates an instance of the proxy class, calls the method WhoAmI, and displays the return value.

Recall from Chapter 6 that the generated proxy class is inherited from SoapHttpClientProtocol. This class defines a property, Credentials, that we will use to set the client's credentials.

Anonymous Access

In the case of anonymous access, any client can connect to the Web server. The IIS server does not expect any credentials from the client.

Here is the client code that accesses the Web service using anonymous IIS authentication:

// Project WebSecurity/MyClient

class MyApp {
     static private readonly String
       MYURL="http://localhost/Demo/MyTest.asmx";

     // IIS is set for anonymous access
     public static void UseAnonymousIdentity() {
       MyTest t = new MyTest();
       t.Url = MYURL;
       String s = t.WhoAmI();
       Console.WriteLine(s);
     }
     ...
}

Note that the client need not specify any credentials to the server.

When you run this program, WhoAmI returns an empty string because ASP.NET does not associate any security context to the managed thread.

Integrated Windows Authentication

Integrated Windows authentication uses a cryptographic exchange with the client to confirm the client's identity. Currently, this communication mechanism is available to only two types of clients—Internet Explorer and .NET applications.

For .NET applications, the class CredentialCache defines a static property, DefaultCredentials, that can be used to pass the client's identity to the server. This is highlighted in the following code excerpt:

// IIS is set for Integrated Windows Authentication
public static void UseDefaultWindowsIdentity() {
     MyTest t = new MyTest();
     t.Url = MYURL;
     t.Credentials = CredentialCache.DefaultCredentials;
     String s = t.WhoAmI();
     Console.WriteLine(s);
}

The property DefaultCredentials represents the system credentials for the security context of the user running the client application. However, if the underlying OS thread is impersonating a user, DefaultCredentials represents the credentials of the user being impersonated.

Basic Authentication

Basic authentication is used for nonsecure identification of clients. The user name and the password are sent to the server as base-64 encoded text. Note that the user name and the password are encoded, not encrypted, in this type of authentication.

A client can deal with this mode of authentication by using the class NetworkCredential, as illustrated in the following code excerpt:

public static void UseSpecificWindowsUserIdentity(
       String user, String password, String domain) {
     MyTest t = new MyTest();
     t.Url = MYURL;
     t.Credentials = new NetworkCredential(user, password,
       domain);
     String s = t.WhoAmI();
     Console.WriteLine(s);
}

The constructor of NetworkCredential takes as parameters the user name, password, and the domain to which the user belongs. This information is passed to the server as base-64 encoded text.

An overloaded constructor of NetworkCredential takes just the user name and the password as parameters. This is useful if basic authentication for the virtual directory is already configured with a default domain name.

There is yet another way to specify the credentials in the case of basic authentication: Use the class CredentialCache and add one or more credentials to it, as illustrated in the following code excerpt:

public static void UseSpecificWindowsUserIdentity2(
       String user, String password, String domain) {
     CredentialCache c = new CredentialCache();
     c.Add(new Uri("http://localhost/"), "Basic",
       new NetworkCredential(MYUSR, MYPSWRD, MYDOMAIN));
     MyTest t = new MyTest();
     t.Url = MYURL;
     t.Credentials =     c;
     String s = t.WhoAmI();
     Console.WriteLine(s);
}

The Add method on CredentialCache takes three parameters. The first parameter is the URI prefix of the Web site. The second parameter represents the authentication method used by the URI. Possible options are basic and digest. The third parameter is the NetworkCredential object that should be used for the specified URI. The idea is to store a collection of credentials for various Web resources as a single unit. When the GetCredential method is called, CredentialCache returns the proper set of credentials, as determined by the URI of the Web resource and the requested authentication scheme. Applications that use a variety of Internet resources with different authentication schemes benefit from using the CredentialCache class, because it stores all the credentials and provides them as requested.

Digest Authentication

Digest authentication sends a hash value over the network rather than the password. This mode was introduced in IIS 5.1 and is not widely supported on other platforms.

It is left as an exercise for you to take the previous implementation and modify it for digest authentication.

Now that we understand the various authentication methods available under IIS, here is a pop quiz for you: Let's say you set the IIS virtual directory to use anonymous access as well as integrated Windows authentication (Figure 9.8). What is the authentication method that you think will be used?

It turns out that if anonymous authentication is enabled along with any other authentication mode, IIS does not, by default, bother to authenticate any user. That is, all users are treated as anonymous users.

This default behavior can be overridden by sending to IIS an HTTP 401 status code (Unauthorized) from the code handling the request. On receiving this status code, IIS will go ahead and authenticate the user.

The following code is the revised implementation of our Web service method. This code returns the 401 status code to IIS if it finds that the identity of the caller is blank:

[WebMethod]
public String WhoAmIEx() {
     String s = Thread.CurrentPrincipal.Identity.Name;
     if (s.Length == 0) {
       Context.Trace.Write("Sending 401 status code");
       this.Context.Response.StatusCode = 401;
     }
     return s;
}

As the code shows, the status code is set on the response object. This object, which is of type HttpResponse, encapsulates all the HTTP response operations for the current HTTP request. Complete information about the current request itself is encapsulated in an object of type HttpContext and can be accessed as the Context property on WebService.

Instead of setting the 401 status code in each of the methods, there is a way to set it at the application level, by means of an HTTP module. HTTP modules are managed classes that implement IHttpModule and reside in the ASP.NET worker process. They get to intercept each request before it reaches your ASP.NET application. For a sample implementation, see [Bro-02].

This concludes our discussion of IIS authentication schemes. A final note on IIS authentication: It is also possible to configure IIS with SSL and use client certificates for authenticating the user. Check the SDK documentation under the topic “Securing XML Web Services Created Using ASP.NET” for a code sample on using client certificates.

ASP.NET Authentication

ASP.NET implements authentication using authentication providers, which are code modules that verify credentials and implement other security functionality, such as cookie generation.

At present, ASP.NET supports three authentication providers—Windows, Forms, and Passport. An ASP.NET application enables a specific authentication provider by means of <authentication> XML tag in the application's Web.config file. A sample configuration that corresponds to Windows provider is shown here:

<configuration>
  <system.web>
    <authentication mode="Windows" />
  </system.web>
</configuration>

Other possible options for the authentication mode are Forms, Passport, and None. The authentication mode None is used when you are not authenticating users at all. In this case, ASP.NET does not associate any security context with the managed threads.

Windows Authentication

This provider relies on the authentication capabilities of IIS. After IIS completes its authentication, ASP.NET associates the security context of the user with the managed thread where the user call is being processed.

Not all IIS authentication modes can be combined with all ASP.NET authentication modes. In the case of ASP.NET Windows authentication, for example, IIS cannot be set to anonymous access.

An excellent article that describes in detail the various authentication modes of IIS and ASP.NET, and their possible combinations, is [Ker-01]. The article also guides you in selecting the right authentication mode for your ASP.NET application.

Forms Authentication

Using this provider causes unauthenticated requests to be redirected to a specified HTML form using client-side redirection. The client can then supply logon credentials and post the form back to the server. If the application authenticates the request (using application-specific logic), ASP.NET issues a cookie that contains the credentials or a key for reacquiring the client identity. Subsequent requests are issued with the cookie in the request headers, which means that subsequent authentications are unnecessary.

Passport Authentication

This is a centralized authentication service provided by Microsoft that offers a single logon facility and membership services for participating sites.

ASP.NET, in conjunction with the Microsoft Passport SDK, provides similar functionality as Forms authentication to Passport users. Unfortunately, the client-side authentication module for Passport has not been released as of this writing.

Custom Authentication

Web services uses SOAP messages for communication and SOAP headers provide a great way of passing out-of-band information that is not related to the semantics of the Web service. Unlike the Body element of a SOAP message, which is processed by the Web service method, the Header element is optional and thus can be processed by the underlying infrastructure.

Interested readers may wish to check the section “Securing XML Web Services Created Using ASP.NET” in the SDK documentation for extending SOAP messages. The article also includes an HTTP module example that provides a custom authentication mechanism using SOAP headers.

Role-Based Security

The ASP.NET authentication mechanism, when appropriately configured, associates a security context of the client principal with the managed thread servicing the client's call. This principal can be accessed by calling Thread.CurrentPrincipal. Essentially, what this means is the same role-based security mechanism that is available to .NET client applications is also available to ASP.NET applications. The following code excerpt for a Web service method illustrates the use of IPrincipal method IsInRole to check if the caller belongs to the local administrators group:

[WebMethod]
public String SensitiveOperation() {
     IPrincipal pr = Thread.CurrentPrincipal;
     bool b = pr.IsInRole(@"BUILTINAdministrators");
     if (!b) {
       throw new Exception("Only administrators allowed!");
     }
     return "Successful operation";
}

It is also possible to provide role-based checks for the entire ASP.NET application by means of the <authorization> XML element in the application's Web.config file. The following sample shows how to allow local administrators to use the application and deny the rest of the users:

<configuration>
  <system.web>
    <authentication mode="Windows" />
    <authorization>
        <allow roles="BUILTINAdministrators" />
        <deny users="*" />
     </authorization>
  </system.web>
</configuration>

In this case, an HttpModule called UrlAuthorizationModule performs the role check and returns a 401 status code to IIS if the check fails, forcing IIS to authenticate an anonymous client or to simply fail the request.

Elements allow and deny can be applied on roles or users. The users list may contain specific names, as shown here:

<allow users="domain1user1, domain2user2, domain3user3" />

Instead of specific names, you can also use wildcards. The configuration mechanism supports two wildcards—* to represent all users and ? to represent anonymous users.

Note the way the allow and the deny elements have been ordered in the configuration file. UrlAuthorizationModule concatenates authorizations, starting from the Web.config in the current directory (if any), back up to the parent directory, all the way to the virtual root. Finally, it adds the authorization information from Machine.config, which by default looks like this:

<authorization>
     <allow users="*" />
</authorization>

For our application, the resulting list would be:

<authorization>
    <allow roles="BUILTINAdministrators" />
    <deny users="*" />
    <allow users="*" />
</authorization>

The UrlAuthorizationModule walks from the top of the list looking for a match. As soon as a match is found, it stops and either allows or denies the user based on the match. Had we not placed the deny element in the authorization list, access would have been granted to any user, making our role-based check worthless.

Code Access Security

The code access permissions that are available to a .NET client application are also available to an ASP.NET application.

ASP.NET Web applications can be further configured by assigning them trust levels. Trust levels are configured using the <trust> element within the configuration file, either at the machine level or at the application level:

<trust level="Full | High | Low | None" originUrl="url" />

The originUrl attribute specifies an application's URL of origin. If present, this can be used for some permissions such as connecting back to the host of origin over the Web. This is important for some ASP.NET applications that require connecting to the origin for proper functioning. Typically, originUrl points to an empty string.

Each level determines the application's permissions, the details of which are specified by an XML security policy file. Each level maps to a specific file that is present in the <FrameworkRootDir>/CONFIG subdirectory.

Full Trust

This level gives the ASP.NET application unrestricted permissions. There is no file associated with this trust.

High Trust

This level provides permissions that grant applications read and write access to the application directory (subject to OS permissions) and allows the application to replace the authentication principal object. It also restricts applications from calling into unmanaged code.

This trust level maps to the file web_hightrust.config.

Low Trust

This level allows applications to read from the application directory and provides limited network connectivity. Applications can connect back to their host site, assuming the originUrl attribute of the <trust> element is configured appropriately.

This trust level maps to the file web_lowtrust.config.

No Trust

This level provides basic execution permission and supports the application's use of isolated storage (a mechanism that allows code to be safely associated with saved data).

This trust level maps to web_notrust.config.

Impersonation

Recall from our earlier discussion that there are two security contexts under .NET, one associated with the managed thread processing the request and one used by the underlying physical thread when it tries to access a secured object such as an NTFS file. If the physical thread is not under impersonation, then the security context of the process is used to access the object.

Under ASP.NET, by default the security context of the user that IIS impersonates is available to aspnet_isapi.dll but is never passed to the ASP.NET worker process (refer to Figure 9.7). As a result, the underlying physical thread used to process the current request is not under impersonation. This means that the identity used to access a secured OS object is that of the ASP.NET worker process. Recall that this identity is ASPNET by default but can be configured via Machine.config.

You can enable impersonation for an ASP.NET application by means of the <identity> element in its Web.config file, as shown here:

<configuration>
  <system.web>
    <identity impersonate="true"/>
  </system.web>
</configuration>

When impersonation is enabled, the aspnet_isapi.dll hands over the impersonation token to the worker process. The physical thread on which your managed code is running will be impersonating the user represented by the token.

The user account being impersonated depends on how the IIS virtual directory has been set for authentication. If the IIS virtual directory is set for anonymous access, this user is IUSR_<SERVER> (by default, but this is configurable). For all other authentication schemes, it is the authenticated client.

ASP.NET provides yet another option for selecting the user to be impersonated. You can specify the user to be impersonated, along with the password, in the configuration file, as shown here:

<identity impersonate="true" userName="domainusr"
     password="pwd"/>

This concludes our discussion on various security mechanisms under ASP.NET. Interested readers may wish to read [Pro-02] for building more secure sites with ASP.NET.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.104.248