Securing XML Web Services Created Using ASP.NET

Deciding which security implementation is best for an XML Web service begins with looking at two key security principles: authentication and authorization. Authentication is the process of validating an identity based on credentials, such as a user name and password, against an authority. Once an identity has been authenticated, authorization determines whether the identity is authorized to access a resource.

XML Web services created using ASP.NET can choose their security options from the authentication and authorization options offered by ASP.NET or customized SOAP-based security. ASP.NET operates in conjunction with Internet Information Services (IIS) to provide several authentication and authorization options. It is also possible to create custom authentication options, such as the use of SOAP headers. Additionally, ASP.NET offers the capability, known as impersonation, to execute a request using the credentials of the client. For details on using impersonation, see ASP.NET Impersonation.

In this topic, the authentication and authorization options available to XML Web services built using ASP.NET are summarized. For complete details regarding the security options available to ASP.NET Web applications, see Securing ASP.NET Web Applications.

Authentication Options for XML Web Services

XML Web services created using ASP.NET have several options for authenticating clients, so the big question is which one is right for a specific XML Web service. In choosing the right security option, one of the things a developer has to choose between is the level of security and performance. For some XML Web services, it is critical that client credentials are sent over the network using encryption, so an algorithm that encrypts the client credentials is essential. For instance, a developer writing an XML Web service that processes credit cards probably worries more about the client credentials being stolen than the extra overhead of encrypting the credit card information.

The following table is a summary of the authentication options available to XML Web services built using ASP.NET. Options prefixed with Windows are a part of the Microsoft Windows authentication options available to XML Web services created using ASP.NET.

Summary of Authentication Options

Authentication option Description
Windows - Basic Use for nonsecure identification of clients, as the user name and password are sent in base 64-encoded strings in plain text. Passwords and user names are encoded, but not encrypted, in this type of authentication. A determined, malicious user equipped with a network-monitoring tool can intercept user names and passwords.
Windows - Basic over SSL Use for secure identification of clients in Internet scenarios. The user name and password are sent over the network using Secure Sockets Layer (SSL) encryption, rather than plain text. This is relatively easy to configure and works for Internet scenarios. However, using SSL degrades performance.
Windows - Digest Use for secure identification of clients in Internet scenarios. Uses hashing to transmit client credentials in an encrypted manner so the password is not transmitted in clear text. In addition, Digest authentication can work through proxy servers. However, it is not widely supported on other platforms.
Windows - Integrated Windows Uses NTLM or Kerberos. Uses a cryptographic exchange with the user's Microsoft Internet Explorer Web browser.
Windows - Client Certificates Use for secure identification of clients in Internet and intranet scenarios. Requires each client to obtain a certificate from a mutually trusted certificate authority. Certificates are optionally mapped to user accounts, which are used by IIS for authorizing access to the XML Web service.
Forms Not supported by XML Web services. This is a system by which unauthenticated requests are redirected to an HTML form using HTTP client-side redirection. Most clients of XML Web services will not want to provide credentials using a UI. You have to use work around this in order to avoid the logon form.
SOAP headers – Custom Useful for both secure and nonsecure Internet scenarios. User credentials are passed within the SOAP header of the SOAP message. The Web server, regardless of the platform hosting the XML Web service, provides a custom authentication implementation.

For all options listed above, except the use of SOAP headers, the security settings are specified using a combination of configuration files and IIS. For details on configuration files, see ASP.NET Configuration. The custom SOAP headers option is detailed following the Authorization section, as that solution involves both authentication and authorization.

Windows Authentication

Both IIS and ASP.NET provide support for authenticating Web applications, including XML Web services, using security built in to Windows. Windows provides three options for authentication: Basic, Digest, and Integrated Windows. Additionally, each option can be used with SSL. As all Windows authentication options except Basic encrypt the data in some form, the additional level of encryption offered by SSL is typically only used in conjunction with Basic or Client Certificates.

Regardless of which Windows authentication option is used, setting up both the XML Web service and XML Web service client is similar. The exception is Client Certificates, so the steps for setting up the server and client to use Client Certificates are covered below separately. No code needs to be added to an XML Web service to use Windows authentication, as the authentication options are set in a configuration file and IIS. Code to pass the client credentials to the XML Web service must be added to an XML Web service client.

If SSL is chosen as part of the authenticating mechanism used by an XML Web service, SSL needs to be configured for the Web application hosting the XML Web service or for the XML Web service itself, using IIS. The service description and, consequently, proxy classes generated from the service description will reflect that the XML Web service uses SSL (if the service description and service help page are accessed using SSL). The URL to the XML Web service within the service description will be prefixed with https. For details on setting up SSL, see the IIS documentation.

To configure the XML Web service for Windows authentication

  1. Configure the XML Web service to use Windows authentication, using IIS.

    IIS allows you to specify security at either the directory or file level. If you want to specify the security for an XML Web service on a per-file basis, set the permissions for the XML Web service on the .asmx file in IIS. The .asmx file is the entry point into the XML Web service. See the IIS documentation for details.

  2. Modify the configuration file to specify Windows authentication.

    Set the mode attribute of the authentication XML element in a configuration file to "Windows". For details on how to configure a configuration file, see ASP.NET Configuration. The following code example modifies a configuration file to use Windows authentication.

    // Fragment of a Web.config file.
    <authentication mode= "Windows">
    </authentication> 
    

To pass client credentials to an XML Web service using Windows authentication

  1. Create a new instance of the proxy class to the XML Web service. If a proxy class has not been generated, see Creating an XML Web Service Proxy for details.

  2. Create a new instance of the NetworkCredential class, setting the UserName, Password and Domain properties.

  3. Create a new instance of CredentialCache.

  4. Add the NetworkCredential to the CredentialCache using the Add method of CredentialCache.

  5. Assign the instance of CredentialCache to the Credentials property of the proxy class.

    If Integrated Windows authentication is used, then you must set the Credentials property to DefaultCredentials.

    When the Credentials property is set to CredentialCache.DefaultCredentials then the client negotiates with the server to do Kerberos and/or NTLM authentication depending on how the server is configured.

    The following code example sets the client credentials passed to an XML Web service method using Windows authentication.

    Imports System
    Imports System.Web.Services.Protocols
    Imports System.Net
    Imports MyMath
    
    Public Class Calculator
       Public Shared Sub Main()
         ' Create a new instance of the proxy class to an
         ' XML Web service method. 
         Dim mathproxy As MyMath.Math = New MyMath.Math()
    
         ' Create a new instance of CredentialCache.
         Dim mycredentialCache As CredentialCache = New CredentialCache()
    
         ' Create a new instance of NetworkCredential using the client
         ' credentials.
           Dim credentials As NetworkCredential = New _          NetworkCredential(UserName,SecurelyStoredPasword,Domain)
    
         ' Add the NetworkCredential to the CredentialCache.
           mycredentialCache.Add(New Uri(mathproxy.Url), "Basic", _                             credentials)
    
         ' Add the CredentialCache to the proxy class credentials.
         mathproxy.Credentials = mycredentialCache
    
         ' Call the method on the proxy class.
         Dim result As Integer 
         result = mathproxy.Add(3,5)
      End Sub
    End Class 
    [C#]
    using System;
    using System.Web.Services.Protocols;
    using System.Net;
    using MyMath;
    
    public class Calculator
    {
      public static void Main() 
      {
         // Create a new instance of the proxy class to an XML
         // Web service method. 
         MyMath.Math math = new MyMath.Math();
    
        // Create a new instance of CredentialCache.
        CredentialCache credentialCache = new CredentialCache();
    
       // Create a new instance of NetworkCredential using the client
       // credentials.
       NetworkCredential credentials = new
          NetworkCredential(UserName,SecurelyStroredPassword,Domain);
    
       // Add the NetworkCredential to the CredentialCache.
       credentialCache.Add(new Uri(math.Url),                        "Basic", credentials);
    
       // Add the CredentialCache to the proxy class credentials.
       math.Credentials = credentialCache;
    
         // Call the method on the proxy class.
         int result = math.Add(3,5);
      }
    }
    

Client Certificate Authentication

Client Certificates help provide a secure mechanism for authentication, as clients are required to send an electronic document, called a client certificate, identifying a client using a SSL connection to the Web server. The SSL connection encrypts the client credentials contained within the client certificate as they are sent over the network. Communication between the client and the Web server is encrypted using a combination of the encryption keys sent by the client and keys provided by the Web server. Once the communication is established, only the client and server computers can communicate to each other using that SSL connection.

A client certificate can be obtained from a certificate authority, which can either be the Web server itself or a trusted intermediary between the client and server. Once a certificate has been obtained, and the Web server has been configured to accept client certificates, a client can send the client certificate to the Web server over a SSL connection, when an XML Web service is called. For further details on client certificates, see the IIS documentation.

To configure the XML Web service for Client Certificate authentication

The following list is an overview of how to configure IIS to authenticate clients using client certificates. For details, see the IIS documentation.

  1. Install SSL.

  2. Configure the Web application to accept client certificates.

  3. Modify the configuration file to specify Windows authentication for the XML Web service.

    Set the mode attribute of the authentication XML element in a configuration file to "Windows". For details on how to configure a configuration file, see ASP.NET Configuration. The following code example modifies a configuration file to use Windows authentication.

    // Fragment of a Web.config file.
    <authentication mode= "Windows">
    </authentication>
    

To pass client credentials to an XML Web service using Windows authentication

  1. Create a new instance of the proxy class to the XML Web service. If a proxy class has not been generated, see Creating an XML Web Service Proxy for details.

  2. Create a new instance of the X509Certificate.

  3. Invoke the CreateFromCertFile method to load the client certificate from a file.

    A client can obtain a client certificate file from a trusted certificate authority. For details, see the IIS documentation.

  4. Add the X509Certificate to the ClientCertificates collection of the proxy class.

    The following code example demonstrates how an XML Web service client passes its credentials using a client certificate. A client certificate issued from the Web server is loaded from a file with the CreateFromCertFile method and then added to the ClientCertificates property of the proxy class.

    ' Instantiate proxy class to a Bank XML Web service.
    Dim bank As BankSession = new BankSession()
    
    ' Load the client certificate from a file.
    Dim x509 As X509Certificate = X509Certificate.CreateFromCertFile("c:\user.cer")
    
    ' Add the client certificate to the ClientCertificates property
    ' of the proxy class.
    bank.ClientCertificates.Add(x509)
    
    ' Call the method on the proxy class, which requires authentication
    ' using client certificates.
    bank.Deposit(500)
    [C#]
    // Instantiate proxy class to a Bank XML Web service.
    BankSession bank = new BankSession();
    
    // Load the client certificate from a file.
    X509Certificate x509 = X509Certificate.CreateFromCertFile(@"c:\user.cer");
    
    // Add the client certificate to the ClientCertificates property
    // of the proxy class.
    bank.ClientCertificates.Add(x509);
    
    // Call the method on the proxy class, which requires
    // authentication using client certificates.
    bank.Deposit(500);
    

Authorization Options for XML Web Services

The purpose of authorization is to determine whether an identity should be granted the requested type of access to a given resource. There are two fundamental ways to authorize access to a given resource: file authorization and URL authorization. File authorization can be used whenever Windows authentication is used, as the permissions are set in IIS on a per-file basis. URL authorization can be used with any of the built-in authentication mechanisms supported by ASP.NET. With URL authorization, configuration is done through a configuration file, where users can be selectively granted or denied access to any files associated with ASP.NET, including .asmx files.

For details on setting up authorization on a per-file basis, see the IIS documentation.

For details on setting up authorization using a configuration file, see ASP.NET Authorization.

SOAP Headers – Custom solution

The Windows authentication mechanisms, including client certificates, rely on the HTTP transport, whereas SOAP is transport-independent. XML Web services built using ASP.NET use SOAP over HTTP. So, one reason to create a custom authentication mechanism is to decouple authentication from the transport. This can be accomplished by passing the authentication credentials in the SOAP header.

SOAP headers are a great way of passing out-of-band or information not related to the semantics of an XML Web service. Unlike the Body element of a SOAP message, which includes the in and out parameters for the XML Web service method, which are thus processed by the XML Web service method, the Header element is optional and can thus be processed by the infrastructure. That is, processed by infrastructure developed to provide a custom authentication mechanism.

The following custom solution is built using ASP.NET to provide an authentication mechanism using SOAP headers. The solution is built using the following steps:

  • A custom HTTP Module is built that runs on the Web server hosting the XML Web services.
  • The HTTP Module parses HTTP messages to check whether they are SOAP messages.
  • If the HTTP Module detects a SOAP message, it reads the SOAP headers.
  • If the SOAP message has the SOAP header with authentication credentials, HTTP Module raises a custom global.asax event.

To modify the XML Web service to use the custom SOAP header authentication, the XML Web service must do two things: specify that it expects the SOAP header containing the authentication credentials and authorize the client access to the XML Web service. In the sample provided, the HTTP Module authenticates the user and sets Context properties that an XML Web service can use to decide whether the client is authorized access to the XML Web service.

An XML Web service client sends it credentials to the XML Web service then by adding the expected SOAP header to the SOAP request and populating it with the client credentials. It is important to note that, in this sample, the text is sent over the network in clearly readable text (it is not encrypted). If clear text is not protected enough for your application, add an encryption algorithm.

The following code example is an HTTP Module that parses HTTP messages for SOAP requests. If the HTTP message is a SOAP message, the custom WebServiceAuthenticationEvent is raised.

using System;
using System.Web;
using System.IO;
using System.Xml;
using System.Xml.XPath;
using System.Text;
using System.Web.Services.Protocols;

namespace Microsoft.WebServices.Security {

      public sealed class WebServiceAuthenticationModule : IHttpModule 
      {
         private WebServiceAuthenticationEventHandler 
                       _eventHandler = null;

         public event WebServiceAuthenticationEventHandler Authenticate 
         {
           add { _eventHandler += value;}
           remove {_eventHandler -= value;}
         }
 
         public void Dispose() 
         {
         }

         public void Init(HttpApplication app) 
         {
           app.AuthenticateRequest += new
                      EventHandler(this.OnEnter);
         }

         private void OnAuthenticate(WebServiceAuthenticationEvent e) 
         {
           if (_eventHandler == null)
               return;

             _eventHandler(this, e);
             if (e.User != null)
                e.Context.User = e.Principal;
         }

         public string ModuleName
         {
           get{ return "WebServiceAuthentication"; }
         }

         void OnEnter(Object source, EventArgs eventArgs) {
           HttpApplication app = (HttpApplication)source;
           HttpContext context = app.Context;
           Stream HttpStream = context.Request.InputStream;

           // Save the current position of stream.
           long posStream = HttpStream.Position;

           // If the request contains an HTTP_SOAPACTION 
           // header, look at this message.
           if (context.Request.ServerVariables["HTTP_SOAPACTION"]                          == null)
              return;

           // Load the body of the HTTP message
           // into an XML document.
           XmlDocument dom = new XmlDocument();
           string soapUser;
           string soapPassword;

           try 
           {
             dom.Load(HttpStream);

             // Reset the stream position.
             HttpStream.Position = posStream;

             // Bind to the Authentication header.
             soapUser =
                 dom.GetElementsByTagName("User").Item(0).InnerText;
             soapPassword =
                 dom.GetElementsByTagName("Password").Item(0).InnerText;
           } 
           catch (Exception e) 
           {
             // Reset the position of stream.
             HttpStream.Position = posStream;

             // Throw a SOAP exception.
             XmlQualifiedName name = new
                          XmlQualifiedName("Load");
             SoapException soapException = new SoapException(
                       "Unable to read SOAP request", name, e);
             throw soapException;
           }

           // Raise the custom global.asax event.
           OnAuthenticate(new WebServiceAuthenticationEvent                        (context, soapUser, soapPassword));
           return;
      }
    }
}

The following code example is the custom authentication event that is raised by the HTTP Module, if a SOAP request is received.

namespace Microsoft.WebServices.Security {
    using System;
    using System.Web;
    using System.Security.Principal;

    public class WebServiceAuthenticationEvent : EventArgs {
       private Iprincipal _IPrincipalUser;
       private HttpContext _Context;
       private string _User;
       private string _Password;

       public WebServiceAuthenticationEvent(HttpContext context)
       {
            _Context = context;
       }

       public WebServiceAuthenticationEvent(HttpContext context,
                       string user, string password)
       {
           _Context = context;
           _User = user;
           _Password = password;
       }
       public  HttpContext Context 
       { 
         get { return _Context;}
       }
       public IPrincipal Principal 
       { 
         get { return _IPrincipalUser;} 
         set { _IPrincipalUser = value;}
       }
       public void Authenticate()
       {
         GenericIdentity i = new GenericIdentity(User);
         this.Principal = new GenericPrincipal(i, new String[0]);
       }
       public void Authenticate(string[] roles)
       {
         GenericIdentity i = new GenericIdentity(User);
         this.Principal = new GenericPrincipal(i, roles);
       }
       public string User 
       {
         get { return _User; }
         set { _User = value; }
       }
       public string Password
       {
         get { return _Password; }
         set { _Password = value; }
       }
       public bool HasCredentials {
         get 
         {
              if ((_User == null) || (_Password == null))
                return false;
              return true;
         }
       }
    }
}

The following code example is the delegate for the custom WebServiceAuthenticationEvent event.

namespace Microsoft.WebServices.Security 
{
   using System;

   public delegate void WebServiceAuthenticationEventHandler(Object sender,  WebServiceAuthenticationEvent e);
}

The following code example is an XML Web service that defines the Authentication SOAP header that a client must pass. The XML Web service does not have to do the authentication. Rather, it can inspect the User.Identity.IsAuthenticated property to determine if the HTTP Module has authenticated the user.

<%@ WebService Language="C#" Class="SecureWebService" %>

using System;
using System.Web.Services;
using System.Web.Services.Protocols;

public class Authentication : SoapHeader {
  public string User;
  public string Password;
}

public class SecureWebService : WebService{
  public Authentication authentication;

  [WebMethod]
  [SoapHeader("authentication")]
  public string ValidUser(){
    if (User.IsInRole("Customer"))
      return "User is in role customer";

    if (User.Identity.IsAuthenticated)
      return "User is a valid user";
    return "not authenticated";
  }
}

The following code example is an XML Web service client that passes the necessary credentials for a custom SOAP header authentication mechanism within an Authentication SOAP header.

    // Create a new instance of an XML Web service proxy class.
    SecureWebService s = new SecureWebService();

    // Create the Authentication SOAP header and set values.
    Authentication a = new Authentication();
    a.User = user.Value;
    a.Password = password.Value;

    // Assign the Header.
    s.AuthenticationValue = a;

      string result = s.ValidUser();
      span1.InnerHtml = result;

See Also

Securing ASP.NET Web Applications | ASP.NET Configuration | Building XML Web Services Using ASP.NET | NetworkCredential | CredentialCache | X509Certificate