HTTP Security and ASP.NET Web Services

 

Scott Seely
MSDN Architectural Samples Team

August 2002

Applies to:
   Microsoft® ASP.NET
   Microsoft® Internet Information Services (IIS)
   HTTP security
   Web services

Summary: HTTP-based security mechanisms are currently the best way to keep your Web services secure. Learn how to secure a Web service using a combination of features found in Microsoft IIS and Microsoft ASP.NET. (14 printed pages)

Note   This article assumes that you are familiar with how to use SSL with IIS.

Contents

Introduction
Encryption and Signing with SSL
Authentication
Using Code Access Security
Interoperability
Summary
Resources

Introduction

One item that seems to be driving developers of Web services crazy is figuring out how IIS and ASP.NET Web services work together to provide security. Today, security is handled by IIS and leveraged by ASP.NET. ASP.NET can take the identity information provided by IIS and use that to know who called or to make use of code access security for specific operations on the Web service. The hard part for many is enabling the .NET application to take advantage of the built-in IIS security features. In the not too distant future, WS-Security will be an even better option. Until that day arrives, HTTP-level security will be what many of us use to make messaging secure.

When executing a Web method in a secure fashion, the following items must be addressed:

  • Confidentiality makes the data opaque to entities listening to the conversation.
  • Integrity provides receivers with the ability to detect changes to the SOAP message.
  • Authentication answers the question "Who is the caller?"
  • Authorization answers the question, "Does the caller have the right to access this Web method?"
  • Nonrepudation proves that an action occurred in order to prevent the client from fraudulently reneging on a transaction.

Many of these security features do not come alone. Authentication allows authorization and nonrepudiation to happen. The confidentiality measures provided by SSL also include mechanisms for integrity and authentication. This article assumes that you are already familiar with how to use SSL with IIS. If you aren't, look at the resources located at the end of this article. I would also recommend that you locate a Microsoft® Windows® Server that has Certificate Server installed (or install Certificate Server on an available Windows Server machine). It will be helpful for following along in the SSL-oriented sections of this article.

Encryption and Signing with SSL

Whenever you need to keep the information in an HTTP-based SOAP message confidential, you should run the service over SSL. This keeps the data in the Web service hidden from any entity looking at data transmission over the wire.

To use the information in this section, you must have an X.509 certificate already set up at the root of your Web server. See the HOWTO: Configure SSL in a Windows 2000 IIS 5.0 Test Environment Using Certificate Server 2.0 (Q290625) for information on how this is done. With a certificate properly installed, you can choose to force SSL authentication for the virtual directory or for a specific file.

To open the Internet Information Services management console

  1. On the Start menu, click Run.

  2. In the Open edit box, type inetmgr.

  3. Click OK.

    The IIS management console opens.

Requiring SSL for the virtual directory or a particular file is a matter of selecting the right options in IIS. To select the "right options," navigate to the virtual directory within the IIS management console. If you want to require SSL for all Web services accessible through a given virtual directory, right-click that virtual directory, click Properties, and then click the Directory Security tab.

To secure only a specific Web service, right-click the .asmx file associated with that Web service, click Properties, and then click the File Security tab. For either procedure, you will see a dialog box similar to what you see in Figure 1. Under Secure communications, click Edit. The Secure Communications dialog box opens as shown in Figure 2.

Figure 1. The Security tab in the IIS management console

Figure 2. Secure Communications dialog box

By default, the Require secure channel (SSL) check box is clear; select it to require SSL. SSL supports both 40-bit and 128-bit encryption. The more bits used by the encryption, the harder it is to break it and figure out what the original bits were. This is all you need to do in order to enable SSL for a specific .asmx file or an entire Web service. By doing this, the communications between any Web service client and the Web service itself will be secure as long as the Web server's certificate is not compromised. SSL uses X.509 certificates that contain a public key and may also contain a private key. If the private key becomes known to any outside party, communications encrypted using the public key are no longer secure from inspection by that outside party.

Once you have the resource set up to require SSL for communications, any messages sent between the sender and receiver will be encrypted and signed. This means that outside parties cannot read the contents of the messages. If an outside party changes the bytes in the message, the message receiver can detect it.

Authentication

To make use of any authentication IIS does for you, you will need to edit the Web.config file associated with your Web service. To have the user's identity available in the HttpContext, you will need to set the /configuration/system.web/authentication/@mode attribute to Windows. The mode attribute must be set whenever IIS uses one of the following authentication options: Basic, Digest, Integrated Windows Authentication (NTLM/Kerberos), or X.509 certificates. The user credentials presented by any of these authentication options must map back to a user that exists either on the local machine or within Active Directory.

The combination of IIS and the right Web.config settings will allow the Web service to discover the caller's identity. As an added bonus, the request context will assume the caller's identity. If you want to make use of Windows authentication, the Web.config file needs to look like this:

<configuration>
    <system.web>
        <authentication mode="Windows" />
        <!-- other elements go here -->
    </system.web>
</configuration>

For handling authorization, auditing, and nonrepudiation, it is critical to turn Windows authentication on. What this does is allow your Web method to run as the caller. Any logging, access checks, and so on are performed based on the user's permissions.

To force IIS to present the caller's identity, you need to tell it to turn off anonymous access. That's all there is to it, really. To do so, go back and open inetmgr (click Start, click Run, and then type inetmgr). Navigate to the virtual directory of interest. Depending on whether you want to enforce identity for all files in the virtual directory or just for one Web service, right-click the directory or .asmx file, and then click Properties. Click the Directory Security tab as shown in Figure 1. Under Anonymous access and authentication control, click Edit. The Authentication Methods dialog box opens as shown in Figure 3.

Figure 3. The Authentication Methods dialog box with Anonymous access disabled

The Authentication Methods dialog box allows you to configure how users can access the virtual directory or file. To pass the user's credentials via the HTTP headers, you can use Basic or Digest authentication. Neither Basic nor Digest authentication provides any mechanism to secure the message. The mechanism for passing the user credentials is defined by RFC 2617: HTTP Authentication: Basic and Digest Access Authentication. Essentially, an HTTP header named Authorization is used to pass the username and password. For Basic authentication, the username/password combination is sent in clear text. Well, not exactly. The username and password are actually sent using base64 encoding, which is simply clear text. For those of you who are not familiar with base64 encoding, it is a way to take binary data and present that data as text. No secrets/keys are used when encoding the data. If you choose to use Basic authentication, you should only accept those credentials over SSL. This protects the Web service and the caller from an entity that was sniffing the channel in an attempt to capture a valid set of credentials.

Another option is to use Digest authentication. If you make this choice, understand that many SOAP toolkits do not support Digest authentication. As a result, you are limiting the number of toolkits that can use the Web service. Use Digest authentication when you want to know the caller's identity, target SOAP toolkits contain support for Digest authentication, and the contents of the SOAP messages are not particularly important. Digest authentication encrypts the caller's credentials using a shared secret called a nonce.

Both Basic and Digest authentication use challenge-response mechanisms. Because of this, several requests and responses will go between the client and the receiver before any Web method invocation even takes place. In Basic authentication, the challenge and response can be fairly quick. In fact, a client can provide Basic credentials ahead of time if it knows that Basic authentication is required. This speed-up can be of marginal value in an SSL-based connection where the server certificate needs to be verified and a session key is established. In Digest authentication, the nonce needs to be exchanged before the credentials are encrypted. Again, this requires some handshaking to occur before any Web service code gets executed.

To force these items to be enabled for your Web service, just select the appropriate boxes on the Authentication Methods dialog. If you want to make sure that you only get authenticated users, make sure that the Anonymous Access check box is clear. Once this is done, you can do the following things server-side:

  • Discover who the caller is.
  • Use code access security to limit who calls which methods.

The following Web service returns the current caller information:

[WebMethod]
public string WhoAmI() {
    return "Running as User : " +
        Thread.CurrentPrincipal.Identity.Name;
}

We will modify a simple console application that calls this Web service. To start, the client looks like this:

static void Main(string[] args) {
    localhost.Sample svc = new localhost.Sample();
    try {
        Console.WriteLine( svc.WhoAmI() );
    } catch ( Exception ex ) {
        Console.WriteLine( ex.ToString() );
    } finally {
        svc.Dispose();
    }
}

When no security is applied to the Web service/application, the Main function prints out the following information:

Figure 4. Running with no security, therefore no identity

If you turn anonymous access off via the dialog shown in figure 3, the client will not be able to access the Web service. Instead, the following error message will appear:

System.Net.WebException: The request failed with HTTP status 401: Access Denied.

Why is this? By default, a Web service proxy does not have any information about the caller or what credentials to pass. Since it cannot authenticate itself, the attempt to call the Web method fails and an exception is thrown. If you want to pass the correct credentials for the current user, the easiest thing to do is pass along the current user's default credentials. The try block in the client needs to be modified to read:

svc.Credentials = 
    System.Net.CredentialCache.DefaultCredentials;
Console.WriteLine( svc.WhoAmI() );

This will allow the proxy to access the Web method because it can take the current user's credentials and present them to the Web method when challenged. The Web service returns the following result:

Running as User : REDMOND\sseely

This will work with both Basic and Digest authentication. The authentication information is only good for one Web service call. In other words, the Web service code cannot call other Web services and impersonate the caller using these mechanisms. Remember that if you choose to require Basic authentication, you should also require an SSL connection for that file so that the user's identity is not exposed to any entity that is monitoring the connection. Sometimes, you will want to access the Web service using an identity that is different from the current user. How do you do that? You set the credentials "manually."

I have a user on my local Web server, sseely2, named Example whose password is Test$123. To set the credentials manually, a CredentialCache must be created. The code using the CredentialCache needs to populate the cache with NetworkCredential objects. When adding a NetworkCredential to the cache, the code needs to specify which URL/authentication type combination should be used to return the given credentials. It is possible to populate the cache with identity information for a number of sites and have the cache intelligently return the right credentials for each site and authentication type. To set up the cache to send the correct credentials for a Basic authentication challenge from the Web service, use the following code:

localhost.Sample svc = new localhost.Sample();
try {
    CredentialCache credCache = new CredentialCache();
    NetworkCredential netCred = 
        new NetworkCredential( "Example", "Test$123", "sseely2" );
    credCache.Add( new Uri(svc.Url), "Basic", netCred );
    svc.Credentials = credCache;
    Console.WriteLine( svc.WhoAmI() );

When passing in the URL to use on the line that contains credCache.Add, you will notice that the URL is taken from the Web service instead of being hard-coded or picked from some other source. I like to code calls to the Add method this way because it takes the least amount of effort to make sure that the Web service endpoint and the endpoint used by the call to Add are identical.

If you want to use the same credentials for Digest authentication, the line to add the information to the credential cache would read:

credCache.Add( new Uri(svc.Url), "Digest", netCred );

Basic authentication will work for users registered on the local machine or for a user registered in the directory. Digest authentication only accepts users registered in a trusted Windows Domain.

Yet another way to authenticate Web service callers is by performing mutual authentication over SSL. The sender and receiver of the SOAP message can exchange certificates and validate each other. The server will have a certificate if it is SSL-capable. The client will have a certificate if one has been issued to the client in some form. If you have a certificate server handy, you will need to issue a certificate to yourself and then map the certificate to your user account via the dialog shown in Figure 2. See Mapping Client Certificates to User Accounts for more information.

If you do have certificates available, they are accessible via the Internet Options applet in the Control Panel. The easiest way to access this applet is through Microsoft® Internet Explorer. If you do not have a certificate installed, you will want to get one now. Just open Internet Explorer and navigate to a Windows Server machine that has Certificate Server installed. The URL you want is http://machine_name/certsrv. Follow the instructions on the screen to request and install a client certificate. Next, in Internet Explorer, on the Tools menu, click Internet Options, click the Content tab, and then click Certificates. You should see a dialog box similar to the one in Figure 5.

Figure 5. Certificates dialog box

You will need to export one certificate so that it can be used for Web service proxy authentication. To export a certificate, click Export to open the Certificate Export Wizard. In the wizard, click Next to accept all the defaults, and then pick a file name to write the certificate to. In my case, I saved the certificate to c:\temp\secSample.cer. Click Next, and then click Finish. Now, we need to associate that certificate with a particular user.

  1. Repeat the procedure you used to require SSL to secure either one or all Web services. (See the Encryption and Signing with SSL section beginning with the procedure on opening the IIS management console.)
  2. Select the Enable client certificate mapping check box and click Edit.
  3. On the 1-to-1 mapping tab, click Add.
  4. Select c:\temp\secSample.cer
  5. In the Map to Account dialog box, set the following items:
    • Map Name: HTTP Sample Mapping

    • Account: Pick a user account. In my case, I selected sseely2\Example.

    • Password: Map to the account's password. In my case, I entered Test$123.

      It's okay if the certificate identity and the identity associated with the certificate do not match. When matching a certificate to an identity, the server simply looks for another certificate in its stores that exactly matches the one received. Why? It's possible that an individual has a client certificate issued by a public certification authority (CA). When using SSL client authentication, the server can map that certificate to an identity on the host machine without needing to be associated with the issuer of that certificate in any manner.

  6. Click to confirm the password, and click OK three more times to close the dialog.

Now, you need to set up the other options in IIS. The first thing you need to do is clear out all the available authentication methods so that the protected resource (.asmx file or virtual directory) has the permissions set as shown in Figure 6.

Figure 6. All authentication methods cleared

Then, require client certificates as shown in Figure 7.

Figure 7. Requiring SSL and client certificates

Finally, the client needs to be configured to load the certificate from a file and present it with the Web service. The System.Security.Cryptography.X509Certificates.X509Certificate class knows how to read a DER-encoded X.509 certificate. To load the certificate and make it available to the Web service, read the certificate in and add it to the proxy's client certificate collection.

static void Main(string[] args) {
    localhost.Sample svc = new localhost.Sample();
    try {
        X509Certificate x509 = X509Certificate.CreateFromCertFile(
            @"c:\temp\secSample.cer");
        svc.ClientCertificates.Add( x509 );
        Console.WriteLine( svc.WhoAmI() );
    } catch ( Exception ex ) {
        Console.WriteLine( ex.ToString() );
    } finally {
        svc.Dispose();
    }
}

As expected, the output is:

Running as User : SSEELY2\example

When authenticating the user using Basic/Digest authentication or X.509, you can also use Access Control Lists (ACL) to define which users have access to the directory. One way to view the ACL for a file or directory is to use Windows Explorer. Right-click the file and then click Properties. On the Security tab you can add and remove users and groups of users, as well as manipulate the things they can do with the files.

You do not always want to add users of the Web service to Active Directory. Instead, it may be preferable to keep this information elsewhere. To solve this problem, two approaches are fairly common. One approach, common to secured Web sites, is issuing each user a username and password. These credentials can then be passed via SOAP Headers and other mechanisms. The Cold Storage sample used custom SOAP Headers and an HTTP Module to provide authentication. Another approach involves creating a custom Logon Web service. Here, the caller logs in over a secure channel such as SSL and receives a token to use when calling other methods on the Web service. This method was used for the Favorites Web service.

Using Code Access Security

So far, we have only looked at ways to uniquely identify the user. Once we know who the user is, we can use this information to authorize the user to access one or more methods within the Web service. The Example user is a member of the group sseely2\SampleGroup. If I want to limit access to the WhoAmI Web method to only members of that group, I can apply the System.Security.Permissions. PrincipalPermissionAttribute attribute. Specifically, I would use the following code:

[WebMethod]
[PrincipalPermissionAttribute(SecurityAction.Demand, 
        Authenticated=true,
        Name=@"sseely2\Example",
        Role=@"sseely2\SampleGroup")]
public string WhoAmI() {
    return "Running as User : " + 
        Thread.CurrentPrincipal.Identity.Name;
}

The previous code is somewhat extreme. It requires that the caller's ID is known, that the caller belong to the group sseely2\SampleGroup, and that caller's name be sseely2\Example. It is more common to require membership in a specific group. Using this technique provides a simple way to grant or deny access to specific Web methods. Use code access security—when guarding access at the .asmx level, using access control lists is not enough.

Interoperability

I'd be remiss if I did not mention something about the interoperability of the security mechanisms discussed above. If you expect non-Microsoft toolkits to access your Web service, the most interoperable/best-tested security mechanism is to use Basic authentication to identify the caller and SSL to encrypt the channel. When using this mechanism with Integrated Windows authentication, you will need to add the user names and passwords to either the Web server's Users or to the appropriate Windows Domain controller. The reason behind this is simple: many Web service stacks do not include an HTTP portion that understands how to handle Digest authentication. In many cases, the SSL/SOAP combination may not contain support for sending a client-side X.509 certificate.

Summary

You can secure a Web service using a combination of features found in IIS and ASP.NET. ASP.NET Web services use a credential cache to respond to requests for various types of authentication.

Basic/Digest authentication and SSL all have the same drawback: they require a few messages to be exchanged between the SOAP message sender and receiver before the message can be sent securely. This handshaking can limit the speed in which SOAP messages are transferred. Speeding things up is just one of the motivations behind the WS-Security specification. WS-Security abandons transport protocol techniques in favor of a message-centric security model. Until WS-Security is widely understood and deployed, HTTP-based security mechanisms are the best way to keep your Web services secure.

Resources