In-Depth

Crosscutting Your Web Service Security

Learn to build a robust, flexible, and secure Web services architecture that leverages the .NET Framework''s existing capabilities and handles security as a crosscutting concern.

Web services allow for an excellent implementation of a service-oriented architecture (SOA). In other words, Web services that encapsulate business functions can be consumed by other systems both within and outside the company (such as by business partners, vendors, and suppliers). As an architect designing these Web services, one of your prime goals is to enforce security. At the same time, you don't want to burden your developers with a cumbersome and abstruse framework. Ideally, they should spend 100 percent of their time creating business services that implement the business logic and rules and not writing any security related code at all. Yes, this is possible, and it's easy to achieve in .NET. In this article, I will discuss a base architecture that realizes all of these goals.

The Three Pillars of the Architecture
The architecture discussed in this article is based on three concepts:

Crosscutting Concerns
A core concept that underlies the architecture involves crosscutting concerns. A "concern" is any concept related to the system. For example, in a banking application, crediting and debiting customer accounts and transferring assets between accounts are concerns. All three of these example concerns are fundamental to the banking system and are consequently classified as "core" concerns. Other concerns in the banking application are related to security, transactions, and logging. These concerns are not specific to any particular process/module within the banking application. Such concerns are therefore classified as "crosscutting" concerns. Typical object-oriented systems do not make a clear distinction between these core and crosscutting concerns because the fundamental unit of design in an object-oriented system is an object, not a concern. Systems that do recognize the distinction between these concerns are known as aspect-oriented systems. In an aspect-oriented system, the fundamental unit of design is a concept. Although aspect-oriented systems are most easily implemented on top of an object-oriented language or platform such as Java and .NET, the methodology of aspects applies equally to structured languages such as C.

Web Service Security Concepts
The problem with Web service security is not that it is particularly difficult to implement. Rather, the problem is that people can't agree on the best way (or specification) to accomplish it. WS-Security, which I use in this article, is one of the more popular specifications available for securing SOAP messages. Microsoft, IBM, and Verisign jointly created WS-Security, and it is an OASIS-approved standard. WS-Security leverages the XML Encryption, Signature, and Canonical standards published by the World Wide Web Consortium (W3C).

WS-Security works by inserting custom XML elements within the header of a SOAP message. These elements are fully defined in the WS-Security specification. The process of (correctly) creating and consuming these header elements can be quite complex. To shield developers from the numerous details involved in using WS-Security, Microsoft created the Web Service Enhancements (WSE) add-on package for .NET. WSE provides a foundation for building applications based on Web services specifications published by Microsoft and industry partners, including WS-Security, WS-Policy, WS-SecurityPolicy, WS-Trust, WS-SecureConversation, and WS-Addressing. The architecture I describe in this article leverages WSE to implement WS-Security. The architecture itself is not tied to WS-Security or to WSE in any way. These are merely used as convenient examples. That said, I believe most .NET developers will probably use WSE for .NET Web Services security because it is available for free from Microsoft.

.NET Custom Attributes
The .NET Framework uses attributes to address various issues. Attributes describe how to serialize data, specify characteristics used to enforce security, and limit optimizations by the just-in-time (JIT) compiler so code remains easy to debug. All of these attributes are built into the .NET Framework. With .NET you can create and use your own attributes to describe your code in practically any way conceivable and to affect runtime behavior in creative new ways. Such attributes are called custom attributes and are essentially traditional classes that derive directly or indirectly from System.Attribute. I will implement the security crosscutting concern as a .NET custom attribute. The .NET Framework also provides the ability to interject custom code into the Web service pipeline using custom attributes through a mechanism called SOAP extensions, which I also use in this article's example architecture.

The Nuts and Bolts
As I previously mentioned, I will use WSE to implement WS-Security. To run this article's sample code, you need to install WSE 2.0.

The first step in implementing the architecture is creating the custom SOAP extension. Listing 1 shows the code. A custom SOAP extension is a .NET class that extends the System.Web.Services.Protocols.SoapExtension class and at a minimum overrides the ProcessMessage() method. Detailing a SOAP extension implementation is beyond this article's scope.

The next step is to create the custom .NET attribute that enables developers to decorate their business services to use the custom SOAP extension. Listing 2 shows this code. A custom attribute for a SOAP extension is a .NET class that extends the System.Web.Services.Protocols.SoapExtensionAttribute class. Pay special attention to the ExtensionType property that returns the name of the class that implements the SOAP extension. Again, detailing a custom attribute implementation is beyond this article's scope.

That's it. Once this architecture is in place, implementing a secure .NET Web service becomes as simple as decorating the various Web methods with the AuthorizedRoles custom attribute and specifying the allowed roles in the attribute declaration. Listing 3 shows an example Web service.

Listings 4, 5, and 6 show the output from a client that calls the different Web methods. In Listing 4's output fragment, the client logs in as "Bob" and attempts to call the OnlyManagers Web method. Bob is not a manager (see the GetRoles() method), so an exception is thrown as expected. In Listing 5's output fragment, the client successfully accesses the Anonymous Web method as an anonymous caller. In Listing 6, the client tries to anonymously access the All Web method that correctly throws an exception.

The beauty of this architecture is that it enforces the role-based security policy before any logic inside the Web method ever executes. As a result, developers can rest assured that their code executes only if the caller has been authenticated and is a member of the proper "role."

Note the use of the following special roles in the AuthorizedRoles attribute:

  • "?" indicates anonymous access and means that the caller does not have to be authenticated to invoke the Web method.
  • "*" indicates "all access" and means that any authenticated user can call the Web method.
  • "" (a blank string) indicates no access and means the Web method cannot be called as a Web service. This special role can be used to ensure that test Web methods created for testing the actual Web methods can never be called in the deployed system.

Finally, multiple roles are specified as a comma-delimited string, such as "Guest,Manager."

Remember a couple of points if you want to use this architecture and the accompanying code in your projects:

  • I created a custom user token manager and configured WSE to use my token manager for authenticating the user credentials. The default WSE user token manager authenticates user tokens against actual Windows users (each user of your Web service must have a Windows account on that machine/domain).
  • The current GetRoles() method implementation in the AuthorizedRolesExtension class returns hard-coded role information. Obviously, a real application would be more sophisticated and would, for example, obtain the information from an Active Directory or a database.

In this article, I presented an architecture to handle security as a crosscutting concern in your .NET Web services. The architecture satisfies an architect's two prime goals: creating a solution with a robust and flexible security model and the seemingly paradoxical goal of abstracting the complexity of such a framework from the business service developers. At the same time, the architecture is simple and lightweight because it leverages existing capabilities in the .NET Framework. And although aspect-orientation theorists could argue that the architecture presented in this article is not aspect-oriented in the purest sense of the term, I contend that it is one in philosophy. As Shakespeare would say, what's in a name anyway?