VSM Cover Story
Simplify Authentication With WSE 3.0
Web Service Extensions 3.0 enables you to take advantage of new turnkey scenarios to provide more robust and easier-to-use authentication mechanisms in your Web services.
- By Martin Kulov
Technology Toolbox: C#, XML, WSE 3.0, ASMX Web Services
Microsoft has been providing tools for building Web services since it released the initial version of Visual Studio.NET in 2000.
As with many Microsoft technology releases, the initial implementation of Web services—so-called ASMX Web services—pointed the way to some fertile solutions, but lacked key aspects of functionality that developers would need to build truly robust and secure solutions. Web Services Extensions 3.0 is a significant new release from Microsoft that enables you to address many of the previous shortcomings of ASMX Web services. One of the key new features is the ability to create turnkey solutions for handling authentication in your Web services. In this article, I'll address how to do this, including the range of options you have for setting authentication levels.
It's instructive to compare what Microsoft makes available now to what it provided when it initially released VS.NET. One thing that has always been true is that Web services are easy to create using Visual Studio. For example, consider the process of creating an ASMX Web service. You begin by creating a Web service project and use the WebService and WebMethod attributes to expose the methods you want to call remotely. The way they work is fairly simple. They send a SOAP message encapsulated in the HTTP body using the HTTP protocol. The response that the server returns is an HTTP packet with a SOAP message in its body.
Setting up a Web service is easy, but securing one isn't. And eventually you'll need to secure access to any Web service of consequence and implement different authentication levels. This is where ASMX Web services fail.
Microsoft has implemented some modifications to ease the debugging process, such as using pure GET and POST methods as defined by the HTTP protocol to access a Web service. These work great, and developers have been able to rely on these since Microsoft introduced ASMX Web services, but today developers face some critical issues that must be addressed.
First, a Web service should be transport agnostic. This means that you should be able to make service calls regardless of the transport protocol you use. You should pass the same SOAP message for every single protocol and let the transport layer care about protocol specifics, whether the transport protocol is HTTP, TCP, UDP, MSMQ, or even SMTP.
The second critical issue that must be addressed: A Web service should be secured from unauthorized access. The SOAP message that you send to and from the server should be encrypted (or at least signed) and access to the service should be provided depending on user credentials. This is where the transport agnostic features really shine. If you provide a mechanism for encrypting and signing a SOAP message or parts of it, it is guaranteed that this mechanism will work, regardless of the transport protocol you use. Even better: You can change different authentication mechanisms, as long as both the client and server know how to process the SOAP message. Unfortunately, ASPX Web services cannot do this because they are closely tied to the HTTP protocol.
Old Versus New Approaches
It might be useful to review how developers handled security in Web services previously, before turning your attention to how you handle them now. To secure an ASMX Web service, you can either implement a custom solution for encrypting the data, use some kind of state service to keep user credentials between requests, or deploy transport-level security such as HTTPS.
One part of this solution might require encrypting the contents of the transferred parameters or acquiring an SSL server certificate. For authentication, you can implement a custom authentication scheme passing a hash key as the first parameter to every single method call. You generate the hash key from your Login method and store it in the database where you can access it. If the key exists in the database and the associated user permissions allow accessing the method, the request is completed successfully. That is exactly what I did in my first Web service project with my development team. Someone told our team back in those days that IIS session state shouldn't be mixed up with Web services, and we believed him.
This turned out to be untrue, but we kept the custom solution anyway. I remember that we spent about one week implementing and debugging this solution because we wanted it to work in a Web farm. Suffice it to say that creating a custom solution also means spending significant amounts of time auditing the coding, testing, and security of your solution.
Microsoft has been active in trying to address these issues since the initial release of ASMX Web services. For example, it provided Web service authentication solutions in Web Service Enhancements (WSE) version 1.0. WSE is an add-on to Visual Studio .NET; and you can find the latest version, WSE 3.0, on Microsoft's Web site (go here). One of the more interesting features of WSE 3.0 is that it introduces an easy-to-use-and-expand framework that provides security and authentication for Web services that rely on it.
To create this framework, Microsoft collected information on the most common ways of securing web services. It also took advantage of the feedback generated on various forums and newsgroups to define common working scenarios. These scenarios represent the most frequent, key ways of implementing Web service security and authentication today. That is why they are called Turnkey Security Scenarios.
The documentation for WSE 3.0 refers to scenarios as assertions, but scenarios is more widely used in the Web service community and better describes their purpose, so I'll use the term scenario instead of assertion. The cool thing about the turnkey scenarios is that you can apply them declaratively, enabling the administrator of the system to change them during service runtime. The declarative model also gives you ways to expand existing scenarios programmatically or even write your own custom scenarios. Another benefit: Your network topology might change—the production server might move to some other hosting company—but you don't need to redeploy your service again.
Turnkey scenarios used in a project are specified in a special policy file. In the policy file you can create a named policy and define your security requirements declaratively. When you use a predefined or custom policy scenario, the policy file must also specify a policy extension class. WSE instantiates this class based on the settings you have specified in the policy file. The sample code from the WSE documentation uses included extensions to load a class so the policy file can be processed properly.
Get Started With the Wizard
Let's begin by using the WSE wizard to enable policy use on your Web service. After you install WSE 3.0, click on Properties in your existing Web service project and select "WSE Settings 3.0" from the context menu. On the first tab, you must check "Enable this project for Web Service Enhancements" and "Enable Microsoft Web Services Enhancements Soap Protocol Factory." On the Policy tab, check the "Enable Policy" setting, and it displays the name of the policy file you need to use. This policy file is created in the same folder as your web.config file. Click on the Add button to add a new application policy and specify some user friendly name such as MyPolicy. This prompts the Wizard to display the WSE Security Settings Wizard. Click on Next. This brings up the Authentication Settings page, where you find the options that interest you most (see Figure 1).
Make sure that you select "Secure a service application." Note that you're working on a Web service project, not the client that accesses the Web service. To create a policy file for the client side, you need to run the wizard again and select "Secure a client application."
The lower half of the wizard gives you four different options for your client-authentication method: anonymous, username, certificate, and Windows. Click on Next and leave all other options set to their default settings. Each authentication method is implemented using WSE 3.0's turnkey scenario. Let's go through each option, and what each choice gives you.
The anonymous authentication method is implemented by the AnonymousForCertificateAssertion class. You use this method when you have anonymous clients. Specifically, you use it when there is no need to know who exactly accesses the server. Your clients aren't authenticated, so you care only that the communication is secure and cannot be monitored or tampered with. This secure communication is provided by the X.509 server certificate. It's the only certificate that you must install on the server to have secure communications in this scenario. The certificate allows encoding and signing either the entire message or only parts of the SOAP message, such as the message body and header. The <protection> element gives you a way to control which parts you want to sign and encrypt. You can see that the server certificate is specified using the X.509 element as a sub element of <serviceToken>. You find the certificate using its name and place of storage; in this case, the WSE2QuickStartServer certificate is located in the Local Machine certificate store. The anonymous authentication method addresses a common scenario, so this method is one of the more commonly used authentication methods (see Listing 1).
The Username authentication method is implemented by the UsernameForCertificateAssertion class. You use this method much as you use the anonymous authentication method, except that you can use it to authorize your clients with a username and password. The authentication method uses integrated Windows security by default. It uses Active Directory if the server relies on it.
Again, the server provides the X.509 certificate referred to in the <serviceToken> (see Listing 2). Once implemented, the client should provide a username and password to access the service. I recommend that you provide the user credentials programmatically, which I'll discuss later.
You use the certificate authentication method when both the client and server use X.509 certificates to authenticate each other. This isn't a common solution because the presence of client certificates requires a PPK infrastructure or, more rarely, having each client purchase a legitimate certificate. There is no reasonable way that a Web site you build can ask every single client to provide a trusted certificate. This solution is more applicable in closed boundaries, internal networks, or communication between subsidiaries where you can implement the PPK infrastructure. Certificate authentication is implemented by the MutualCertificate11Assertion class, which requires that you use the WS-Security 1.1 specification. If your solution needs WS-Security 1.0 and 1.1, you should replace the MutualCertificate11Assertion class with the MutualCertificate10Assertion class. The documentation refers to this class as MutualCertificate10.
Use Windows Authentication
Windows authentication is the last option on the wizard's page. This authentication page uses a separate Kerberos credentials server that the client and server can ask to verify the credential information. The credentials server issues Kerberos service tickets, which the SOAP message encapsulates. The receiving side can ask the Kerberos server whether the ticket that it has received remains valid. You can configure the Kerberos servers to allow trust delegation, and this makes them useful to implement corporate, worldwide level security or single sign on (SSO). The WSE wizard doesn't let you configure the settings for the Kerberos ticket, but you can do this manually in the policy file. A Kerberos element looks like this (see Listing 3).
WSE 3.0 provides one additional turnkey scenario, but it isn't available in the wizard. It is implemented by the UsernameOverTransportAssertion class and allows client authentication with a clear text username and password that leaves message encryption and protection on the transport level. However, WSE doesn't check whether the transport level security is used, so be careful when you use this scenario. You don't want your passwords to be easily sniffed on the wire. Using the UsernameOverTransport turnkey scenario is simple:
Unfortunately, this simple scenario relies primarily on the transport level for message protection, so there is no way that you can protect parts of the SOAP message—the message must be either all encrypted or all unencrypted.
I've mentioned that I prefer to set Usernames and passwords programmatically. It is possible to set these in the policy file, but this is not a recommended way of specifying user credentials when using the UsernameOverTransport turnkey scenario because a password should never be left as plain text.
After you create the policy for the service, you should do the same for the client. Having both of the policies ready enables you to apply them with the PolicyAttribute on the Web service class, as well as on the Web service proxy class:
You can also call the method proxy.SetPolicy("MyPolicyUsername") for the Web service proxy class. Setting the username and password for client proxy is specified in the WSE documentation:
// This is the client's credentials that is set in code.
UsernameToken token = null;
string username = Environment.UserName;
string password = GetUsersPassword(username);
token = new UsernameToken(username, password);
// Username and password are set through code.
This quick review of your turnkey options using WSE 3.0 should be enough to get you started using more robust and easier-to-use authentication options in your applications. But there are a couple things I should point out before you try them out.
First, it's important to keep in mind that Web services are designed to be stateless. They usually do not keep session state between requests. This means that the authentication process should be repeated for every call you make. To get better performance, WSE 3.0 implements a WS-SecureConversation specification, which provides a symmetric key for faster authentication after your first authenticated SOAP message.
Also, the WSE 3.0 Settings tool includes a Diagnostics tab where you can set files that store input and output SOAP messages during your testing. I strongly recommend that you use this option and see what your messages look like. This can help you understand the effect of every option that you set in the policy file better.
Read up on how to implement Web service authentication solutions using WSE 1.0 and later here