Sunday, September 30, 2007
WebClientProtocol.Timeout Property
Indicates the time an XML Web service client waits for a synchronous XML Web service request to complete (in milliseconds).
Namespace: System.Web.Services.Protocols
Assembly: System.Web.Services (in system.web.services.dll)
Setting the Timeout property to Timeout.Infinite indicates that the request does not time out. Even though an XML Web service client can set the Timeout property to not time out, the Web server can still cause the request to time out on the server side.
Example
The following example sets the Timeout value to 15000 (15 seconds) for the synchronous call to the Add XML Web service method within the Math XML Web service.
math.Timeout = 15000;
WebClientProtocol Class
The properties of the WebClientProtocol class are used to control the behavior of the transport used to transmit the XML Web service request and response. The properties on this class map to properties found on WebRequest. Instances of classes deriving from WebRequest, such as HttpWebRequest, are used as the transport mechanism for XML Web services created using ASP.NET.
To communicate with an XML Web service, you must create a proxy class deriving indirectly or directly from WebClientProtocol for the XML Web service you want to call. Instead of creating the proxy class manually, you can use the Wsdl.exe tool to create a proxy class for a given XML Web service's service description. Since WebClientProtocol is the base class for your client proxy, you will find its properties on your proxy classes. These properties are useful for controlling the request behavior of the underlying transport.
Verizon Supported : NET Framework 3.0 , 2.0 , 1.1 , 1.0
Namespace: System.Web.Services.Protocols
Assembly: System.Web.Services (in system.web.services.dll)
Setting the Timeout property to Timeout.Infinite indicates that the request does not time out. Even though an XML Web service client can set the Timeout property to not time out, the Web server can still cause the request to time out on the server side.
Example
The following example sets the Timeout value to 15000 (15 seconds) for the synchronous call to the Add XML Web service method within the Math XML Web service.
math.Timeout = 15000;
WebClientProtocol Class
The properties of the WebClientProtocol class are used to control the behavior of the transport used to transmit the XML Web service request and response. The properties on this class map to properties found on WebRequest. Instances of classes deriving from WebRequest, such as HttpWebRequest, are used as the transport mechanism for XML Web services created using ASP.NET.
To communicate with an XML Web service, you must create a proxy class deriving indirectly or directly from WebClientProtocol for the XML Web service you want to call. Instead of creating the proxy class manually, you can use the Wsdl.exe tool to create a proxy class for a given XML Web service's service description. Since WebClientProtocol is the base class for your client proxy, you will find its properties on your proxy classes. These properties are useful for controlling the request behavior of the underlying transport.
Verizon Supported : NET Framework 3.0 , 2.0 , 1.1 , 1.0
Friday, September 28, 2007
Defining Policies for Web Services
the specifications that I have covered thus far are useful in the functionalities that they bring to Web services, the Web Service Description Language (WSDL) wasn’t designed to describe many of these Web service requirements. For example, if the GetDocuments method of our DocumentService required all requests be encrypted and signed with particular X.509 certificates, we would have to define this in something other than WSDL. In this chapter, I will discuss the WS-Policy specification, which provides the necessary framework for both parties in a Web service-based interaction to declare their respective policy requirements. I will also discuss related specifications, such as WS-SecurityPolicy, WS-PolicyAssertions, and WS-PolicyAttachment, which integrate the existing feature-based specifications with the policy framework. I will also show how Microsoft Web Services Enhancements (WSE) implements these policy specifications.
Why We Need Policies
It’s all very well that we now have specifications such as WS-Security and WS- Attachments that prescribe how to handle tasks necessary for SOAP messaging, but those specifications don’t really answer the question of how we expose these functionalities to our Web service consuming clients. We have seen in previous chapters that a Web service request can contain a set of credentials that can be used by the requested service to determine whether to allow access. That’s great, but how do you know which credentials to include in the request message? Should you send along a Kerberos ticket, and if that doesn’t work, try a X.509 certificate, or maybe load the message down with every possible security token that you can assemble? Although a message loaded down with WS- Security-defined elements might be more secure, it's inefficient to perform the extra work of adding these security elements when the recipient doesn't require them and possibly can't even handle such a message.
At this point, I know that many of you have just developed—in your minds—some sort of service, which for fun you might have named MyInfoService, that can be called in advance of making a request to the service that you need to access, say MyDataService, in order to get the applicable policy requirements. MyInfoService might tell you which security tokens to send to MyDataService, which languages are supported, what the message encoding should be, and the like. After this neat epiphany, you might then start to think about the difficulties of late binding to such a service, the extra logic that will need to be built into the client to assemble the right information at run time to make the request to MyDataService. After thinking more about the costs of making an extra SOAP request to MyInfoService before calling MyDataService, you might begin to pine for the early-binding benefits of consuming a WSDL file at design time.
Limitations of Existing Description Mechanisms
As we discussed in Chapter 1, one of the major benefits of WSDL is that it lets us consume the description of a Web service at design time so that we know, at run time, how to interact with the service. Unfortunately, although WSDL is designed to describe items such as exposed Web methods, data types, and SOAP message elements, it has proven less useful when it comes to describing choices, preferences, and other requirements not tied directly to the messaging interface.
For example, WSDL can tell you that MyDataService expects a SOAP request message to have a body element, which contains a certain child element, which corresponds to an exposed Web method. It can also provide schema information for this element. Each exposed Web method will have this information, and there will be similar information about what will be returned should the methods execute successfully. WSDL can even tell you that certain header elements are required, such as those that might provide authentication information or otherwise identify the requester or a particular message. Unfortunately, WSDL alone can’t help us in the case of conditional requirements and other policy issues not directly related to either message transport or describing a SOAP message. Fortunately, WSDL does provide the extensibility to support the definition of policy requirements, as we will discuss shortly.
Defining Web Service Requirements
Up to this point, I have taken a somewhat compartmentalized approach to my discussion of Web service specifications and their related functionalities. This is in part for clarity’s sake but also because the modular design of these specifications enables us to treat them in this manner, implementing the ones that we feel are necessary for our application and simply forgetting the rest. However, when we get into real-world scenarios, things can and will get more complicated. It’s certainly not unanticipated that, in an enterprise Web service, we’ll be carrying out operations such as encrypting and signing SOAP messages, transmitting security tokens, and providing custom routing information. Of course, as I have pointed out, the real question when accessing Web services is not, “How do I do these things?” since the specifications tell us how, but rather, “Which of these must I specify for my request message to successfully return me my ‘Hello World?’” Or, “How can I tell the customers of my Web service what they need to do to get their ‘Hello World?’” Alternatively, how can customers tell my Web service about the messaging requirements of their client applications so that messages can also be received by the clients?
The answer is that we need a way for communicating the full breadth of what our Web service requires of its clients, which can be thought of as our Web service’s policy statement. The same goes for communicating the client’s requirements to the service. Since both parties in a Web service-based interaction need to be able to enforce their own policies and interpret other policy statements, it really makes more sense to discuss policy in terms of sender and receive rather than client and service.
The policy-related specifications, backed by IBM, Microsoft, BEA Systems, SAP and others, officially define policy as “the collection of all acceptable policy assertions for a specific domain” (WS-Policy Section 3.4). In this definition, an assertion is considered a requirement or preference of the service. The following are examples of some assertions that can be made by a Web service:
You must send either an X.509 certificate from you or else a Kerberos ticket, and Kerberos tickets are preferred.
You must encrypt your message header.
You must sign the message.
You must send requests only in either German or Dutch.
You can send messages encoded in either UTF-8 or ANSI_X3.4-1968.
In the rest of this chapter, I’ll discuss the specifications that describe how to declare such policies for our Web services. Like all of the specifications that I discuss in this book, a main goal of these specifications is to be modular, wherein each layer is broken down into a separate specification, which includes the policy specifications described next.
WS-Policy
This specification describes a framework for defining and communicating the expectations and requirements both for sending messages to and receiving messages from a Web service, and it defines the format of such requirements.
WS-PolicyAssertion
This specification defines a core set of Web service-policy assertions dealing mostly with messaging issues, encoding, and other requirements that fall outside WSDL.
WS-SecurityPolicy
This specification describes a set of policy assertions that defines the types of security features that a Web service has implemented and the type of security that’s required of an incoming request message. Although WS-SecurityPolicy is often considered a security specification, I discuss it in this chapter because it does define policy assertions regarding Web services security and leverages the WS-Policy framework for describing assertions.
WS-PolicyAttachment
This specification, in essence, ties everything together by defining how policy expressions are linked to WSDL and Universal Description, Discovery, and Integration (UDDI) as well as to other discoverability mechanisms.
Not only are the above specifications designed, to leverage one another, but they are also orthogonal to one another. This means that, for example, the security policy assertions defined in WS-SecurityPolicy can be taken advantage of in a framework other than the one described in WS-Policy or even just passed around in SOAP message headers. In subsequent sections, I will delve into what these specifications describe in a bit more detail, and we will see how WSE makes it work. I do cover UDDI-specific policy bindings in this book.
Web Services Policy Framework
As the highest level of these policy-related specifications, the WS-Policy specification describes the framework that can be used to combine various policy assertions, each one of them an XML node, into a single policy expression. Applications and services can then use this policy expression to determine the requirements for accessing a Web service provider, and a policy expression might apply to one or more Web methods supported by a Web service or even be used with multiple Web services across an entire domain. As such, multiple policy expressions can be combined into an overall policy document for an entire entity, such as a domain or a company.
Although they might sound complicated, policy expressions are simply hierarchical structures of XML nodes, in which these nodes are either policy assertions, policy operators, or other policy expressions. The structure of a policy expression conveys the ultimate requirements for accessing the service. For example, you can create a policy expression that says a valid request message must have property A, and if not, it must have properties B and C.
The following is an example of a simple policy expression that makes two security assertions:
wsse:X509
In this statement, the two assertions are that a request message must include a valid X.509 certificate that can be used to identify the sender and that the message must be digitally signed using the Advanced Encryption Standard (AES) algorithm so that the recipient can determine whether the message has been tampered with. As you can see, a policy statement is simply an XML structure that combines one or more assertions using policy expressions.
Policy Operators
Much like logical or arithmetic operators, policy operators allow us to define relationships among multiple assertions in a policy expression. WS-Policy defines the following operators, which themselves can contain other nested operators.
All
This element dictates that all assertions or other expression elements that are child elements must be satisfied in order to access a Web service. In the following example, All indicates that an X.509 certificate must be included in the request and that the message must be encrypted and digitally signed:
wsse:X509 wsp:GetNodesetForNode(wsp:GetBody(.))
ExactlyOne
This element dictates that only one of the requirements prescribed by the contained assertions is to be followed when the service is accessed. For example, the following policy expression states that the requesting application must provide only one of three types of security tokens for authentication:
wsse:Kerberosv5TGT wsse:X509v3 wsse:UsernameToken
Note that when the ExactlyOne operator is used, it overrides the Usage attribute value, which in this example is Required. When using the ExactlyOne operator, it’s helpful to also specify the optional Preference attribute on the contained assertions to give requesting applications an idea of which of these assertions are preferred. A higher Preference value is more preferable than a lower value.
OneOrMore
This element dictates that at least one of the requirements prescribed by the contained assertions must be followed when accessing the service. For example, the following policy expression states that the requesting application need provide only one of the three types of security tokens for authentication but can provide more if desired:
wsse:Kerberosv5TGT wsse:X509v3 wsse:UsernameToken
Like ExactlyOne, the OneOrMore operator benefits from having the Preference attribute specified on the contained assertions.
PolicyReference
Although not technically a policy operator, the PolicyReference element provides a means whereby one policy expression can be included, by reference, as an element in another. This element is treated as an All element with respect to the assertions and operators in the policy expression that references it. For a policy expression to be referenced, it must have a Uniform Resource Identifier (URI) assigned by the Id attribute or else a qualified name assigned to the Name attribute. The following is an example of a policy expression that references two other expressions, one by name and one by URI:
wsse:X509v3
wsse:Kerberosv5TGT
Policy Assertions
Assertions are the building blocks of a policy expression, and they describe the rules that an application must follow when making requests to a Web service. Policy assertions follow this general XML format:
* ⋮
... ?
General Messaging Assertions
The WS-PolicyAssertions specifications define a set of policy assertions that apply to the general messaging behaviors of SOAP-based Web services. All of these assertions follow the general format defined in WS-Policy and are defined next.
TextEncoding
If a Web service supports more than one character encoding or requires a nonstandard encoding, the TextEncoding assertion can be used to define such a policy, provided the XML specification supports the encoding. Of course, to maximize interoperability, the default encoding of UTF-8 should be used. The following sample is a policy expression that declares an option between two supported character encodings:
wsp:Preference="1" />
Language
If a Web service supports multiple languages for textual content or if English isn’t the standard, the Language assertion can be used to declare such policies. The following policy example declares that both English and German are supported:
SpecVersion
This assertion enables a Web service to specify which versions of a specification request messages must conform to, where a specification is referenced by its URI.
MessagePredicate
This assertion is used to ensure that request messages contain the necessary XML elements in order to comply with a Web service’s requirements. This assertion is particularly useful for declaring requirements for proprietary elements in the message header or body elements. The message predicate is selected using either XPath 1.0 or the message part selection functions defined in WS-Policy, which are used to select either the entire message body (wsp:Body) or a specified message header (wsp:Header). The selection language used is specified in the Dialect attribute; if this attribute is not specified, the default, XPath 1.0, is assumed. While the two message part functions are easier to use, XPath should be used to select specific parts of a body or header element. The WS-PolicyAssertions specification introduces nine new XPath functions for selecting parts of a SOAP message, which I won’t detail here. In the following assertion, an XPath expression, which uses the new GetHeader function, identifies messages with only one MyCustomHeader header element:
count(wsp:GetHeader(.)/MyCustomHeader) = 1
Note that the use of XPath expressions to select the message predicate is not supported by WSE 2.0.
Security Policy Assertions
While there is certainly a need for general messaging assertions, the drive to declare security requirements to enable interoperability with the WS-Security specification was certainly a major impetus behind the development of WS-Policy. Thus an entire specification, the WS-SecurityPolicy specification, is devoted to defining security assertions. These assertions, most of which are supported by WSE 2.0, are defined next.
SecurityToken
This assertion is used to declare which types of security tokens, defined in WS-Security, must be supplied in order to authenticate a requesting application. Usage of the security tokens listed in Table 6-1 can be specified by this assertion.
Table 6-1: Security Tokens with Defined Policy Assertions Qualified Name
Token Type
wsse:X509v3
X.509 Certificate
wsse:Kerberosv5TGT
Kerberos ticket-granting ticket (TGT)
wsse:Kerberosv5ST
Kerberos service ticket
wsse:UsernameToken
Username token
wsse:SAMLAssertion
SAML Assertion
wsse:XrMLLicense
XrML License
In this table, SAML refers to an XML-based security-assertion token.
WS-SecurityPolicy supports additional optional elements for specifying additional information about token requirements, including which certificate authorities and key distribution centers are acceptable and any token-specific information that makes it easier for requesting clients to present the correct token in the request message. For example, the following policy expression defines acceptable tokens as X.509 certificates from one of two different certificate authorities: a Kerberos TGT from the specified key distribution center or a username token with the specified username.
wsse:X509v3 My Trusted Root Authority cn = VendorXYZ wsse:X509v3 Alternate Trusted Root Authority cn = VendorXYZ
wsse:Kerberosv5TGT My KDC VendorXYZ VendorPOService wsse:UsernameToken VendorXYZ
For more information about details and usage of security token assertions, refer to the WS-SecurityPolicy specification.
Integrity
When a Web service requires that incoming request messages be digitally signed to indicate whether they have been tampered with or modified, an Integrity assertion can be added to the policy expression. The following is an example of an assertion that the message header must be digitally signed with an X.509 certificate using the Secure Hash Algorithm (SHA) algorithm:
wsse:X509v3 wsp:GetNodesetForNode(wsp:GetHeader(.))
For more information about details and usage of integrity assertions, refer to the WS-SecurityPolicy specification.
Confidentiality
If the Web service requires that any parts of a request message be encrypted, this can be stated in the policy expression using a Confidentiality assertion. A Confidentiality assertion that declares that the EncryptedElement node in the message body must be encrypted using the service’s X.509 certificate and Triple-DES (Data Encryption Standard) specification might look like this:
wsse:X509v3 My Trusted Root Authority cn = InvoiceService wsp:GetNodesetForNode(GetBody(.)/EncryptedElement)
Visibility
A request message to a Web service can be routed through one or more intermediate Web services. If any of these intermediaries need to be able to access part of the request message, a Visibility assertion can be made to prevent that portion of the message from being encrypted. The following example assertion requires that the message header not be encrypted:
wsp:GetInfosetForNode(wsp:GetHeader(.))
SecurityHeader
This assertion specifies requirements of the format of the Security header element in a request message. The following example requires that child elements within the Security header be prefixed by the WS-Security XML namespace and that only encrypted elements specifically referenced in the Security header will be processed:
MessageAge
This assertion defines the maximum number of seconds a message has to travel from the sender to the receiver. The total travel time is determined by using the value of the Timestamp header, which makes it mandatory when this assertion is used. If the message transmission time exceeds the value of the Age attribute, the message is rejected. The following is an example of this assertion with an age of 30 minutes:
Obviously the smaller this value, the more important it becomes for the two endpoints to have their times synchronized. The means by which these machines synchronize their times is not addressed in any of the specifications addressed by this book. Rather, time synchronization is normally accomplished via some out-of-band mechanism like a phone call or use of a common time server.
Reliable Messaging Assertions
The WS-ReliableMessaging specification, which I described in Chapter 4, defines a set of policy assertions that are used when declaring the message reliability features that must be used when accessing a Web service endpoint. These assertions include the following:
Specification Version This assertion is used to declare the version of WS-ReliableMessaging to which a message must conform. This assertion is made by the SpecVersion element, as in the following:
Delivery Assurance This assertion is used by the Web service that is the ultimate recipient to declare message delivery behaviors when receiving messages from the service guaranteeing reliability. This assertion is made by the DeliveryAssurance element, where the Value attribute can be one of these values:
wsrm:AtMostOnce Messages are delivered only once.
wsrm:AtLeastOnce Messages are delivered at least once.
wsrm:ExactlyOnce Messages are delivered exactly once, which is equivalent to both of the previous assertions together.
wsrm:InOrder Messages are delivered in the order they were sent.
For example, the following assertions request that messages be delivered by the reliability service exactly once and in the order that they were sent:
Sequence Expiration This assertion declares the time at which a reliable messaging sequence is no longer valid. This assertion is made using the wsu:Expires element. The following assertion indicates that a sequence expires on December 31, 2005:
2005-12-31T12:00:00.000-00:00
Inactivity Timeout This assertion declares the number of milliseconds before an inactive sequence expires. The Milliseconds attribute of the InactivityTimeout element is used to specify this inactivity period. The following assertion indicates that a sequence will expire if it is inactive for longer than 1 minute:
Retransmission Interval This assertion declares the number of milliseconds before an unacknowledged message is re-sent. The Milliseconds attribute of the BaseRetransmissionInterval element is used to specify this period. Another related assertion, ExponentialBackoff, is used to declare that a previously agreed upon algorithm is used when a new retransmission interval is negotiated. The following assertion indicates that a message must be re-sent if an acknowledgement has not been received within 1 minute and that the back- off algorithm should be used:
Acknowledgement Interval This assertion declares the number of milliseconds before an acknowledgement must be sent in a separate message. The Milliseconds attribute of the AcknowledgementInterval element is used to specify this period. The following assertion indicates that a stand-alone acknowledgement message must be sent if another message is not being returned to the sender within 1 minute:
Referencing Policy Expressions
Of course, it’s not enough that we can simply create policy expressions. Just as important are mechanisms for communicating policy expressions to other parties and for applying them against sent and received messages. In fitting with the Web service model promoted by WSDL, the best way to communicate policies are to make them discoverable and accessible to the parties who must access a Web service endpoint. We already have mechanisms defined for publishing Web service definitions, such as WSDL and UDDI. However, we need to be able to locate policy files and reference policy expressions from these description-and-discovery documents. The WS-PolicyAttachment specification provides mechanisms for including policy expressions within an XML document, such as a SOAP message header, or referencing them from an XML document, such as a WSDL file. By linking policies to WSDL, we can leverage the discoverability, description, and extensibility features of WSDL.
Before I discuss how to link policies to WSDL, I need to discuss some of the semantics for identifying policies. While policy expressions are the integral unit for declaring policies, when publishing multiple policy assertions, a service may want to store all policy assertions in a single document, even when these policies relate to multiple endpoints implemented by the service. WS-Policy provides a way to reference a single policy expression within an XML document containing multiple expressions using either a qualified name or a URI.
To reference a policy expression using a URI, the Id attribute must be supplied, as defined by the Web Service Security Addendum specification. This enables the policy expression to be referenced relative to a base URI plus the pound sign and the name assigned in the Id attribute. For example, you can reference the following policy expression in the http://example.com/policy namespace as http://example.com/policy#MySecurityPolicy:
⋮
⋮
⋮
The other mechanism for referencing a policy element involves assigning a qualified name to the policy expression, which is done using the Name attribute as defined by WS-Policy. When the Name attribute is used to specify policy expressions, an expression named MyTextEncodingPolicy in the http://example.com/policy namespace is referenced as p:MyTextEncodingPolicy, where xmlns:p=”http://example.com/policy”.
To hook up policy expressions to other XML documents, the WS-PolicyAttachment specification defines two mechanisms for referencing policy expressions. The first enables a policy expression to be referenced within any arbitrary XML element. This mechanism lets us add a PolicyURIs attribute to any XML element and reference the URI for a policy expression. Likewise, the PolicyRefs attribute can be added to any XML element to reference a policy expression by its fully qualified name. The following example element references these policy expressions that I have just discussed:
The other mechanism enables a policy expression to be assigned to an arbitrary Web service resource; such resources can be specified using the semantics of the WS-Addressing specification, which I discussed in Chapter 4. Known as a policy attachment, this XML policy structure is composed of the following elements:
A reference to the resource to which the policy statement applies, where this reference is based on WS-Addressing.
A policy expression either defined by a Policy element or referenced by a PolicyReference element.
Security information, defined using WS-Security, such as a digital signature.
In the following example, the PolicyAttachment element maps two policy expressions that reference Web services, wherein the security token policy is contained within the policy attachment document and the message age policy is referenced externally:
http://example.com/myservice x:myPortType x:myService wsse:X509v3 My Trusted Root Authority cn = VendorXYZ
Policy Assertions and WSDL
Now that you have the means to define policies for particular Web service resources, you need to add this information to your WSDL files so that it can be publicly exposed to applications that consume your services. The ideal method for publishing a policy assertion for a given resource is to add it to the part of the WSDL file that defines the section of the Web service to which policy applies. However, when doing so, it is important to remember that policy assertions made on parent WSDL elements are enforced on child elements, which means that we must use care when applying policies at multiple, overlapping levels in a service definition because we may get unexpected results for the effective policy for a given WSDL element. For example, when a policy assertion, say a signature requirement, is defined at the message level of the service description, this policy will apply to all parts of the message. However, when a policy, such as confidentially requirement, is also defined for a single message part, the effective policy for that part of the message will include both the signature policy, defined for message, and the confidentiality policy, defined on the part. This can get even more complicated when you define a policy for the portType or operation elements to which the message belongs. In such cases, the effective policy for a message part is determined as the sum total of the policies defined for the portType, operation, message, and part elements.
To make things even less straight-forward, because of restrictions in WSDL 1.1, you aren’t able to add arbitrary XML, such as a PolicyAttachment element, to portType and message elements. Instead, WS-PolicyAttachment specifies that a required UsingPolicy element be added to the WSDL definition to indicate that policies are being defined. In addition, policy references are added to the appropriate elements in the WSDL document for the Web service functionalities for which policies are being defined. When consuming a policy annotated WSDL file, you must be able to retrieve the externally referenced policy files. For example, the following WSDL file includes policy information for the DocumentService Web service:
⋮
⋮
Retrieves XML documents using DIME
⋮
wsse:X509v3 My Trusted Root Authority cn = VendorXYZ ⋮
Stores XML documents for search and retrieval
In this example, any requests to the GetDocument method must contain a X509-based security token, and all parts of the request message to the GetDocument Web method must conform to a signature policy referenced by the SignaturePolicy identifier. In addition, the body of the request message must conform to the encryption policy referenced by the EncryptionPolicy identifier. This makes the effective policy for the body of a request message to the GetDocument method the same as the following:
wsse:X509v3 My Trusted Root Authority cn = VendorXYZ wsp:GetNodesetForNode(wsp:GetBody(.))
Securing Policy Expressions
As publicly exposed in XML documents, policy expressions and policy attachments are exposed to the same kinds of vulnerabilities as SOAP messages being transported using an unencrypted transport; therefore, policy expressions require the same kind of security considerations that are given to SOAP messages. For example, if I’m consuming a policy expression for a Web service, I need to be able to know that this policy expression has indeed been published from the Web service that I’m expecting and hasn’t been modified by any malicious parties. To ensure this, the Web service must digitally sign its policy expressions using a trusted security token (either a shared secret or a private key) as defined in WS-Security. In a similar manner, if I need to protect my Web service’s policies but also make them publicly available, I can encrypt parts of the policy expression using XML encryption as defined in WS-Security.
Because you can apply the features of WS-Security to a policy expression as you would a SOAP message, WS-Policy supports including the Security element, as defined by WS-Security, within a policy expression. When defined in this way, the Security element specifies the security features that are implemented in the policy expression. For example, the following policy expression includes a digital signature, which is defined in the Security element:
wsse:X509 wsp:GetNodesetForNode(wsp:GetBody(.))
MIIEZzCCA9CgAwIBAgIQEmtJZc0rqrKh5i... not(ancestor-or-self::ds:Signature) A1b2C3d4E5f6... BL8jdfToEb1l/vXcMZNNjPOV...
In this example, the NULL value of the URI attribute in the Reference element indicates that the entire expression will be included in the signature. However, the act of signing will change the Signature element and, thereby, the contents of the expression, so I specify the referenced XPath transformation to indicate that the digital signature doesn’t include the Signature element.
Policy Support in WSE
Version 2.0 of WSE includes the ability to declare and enforce WS-Policy– based restrictions on Web services that are enabled to use WSE. While WS- Policy implies that policy is something that applies only to inbound messages, WSE has been designed to apply WS-Policy functionality to outbound messages as well. This enables you to place the same type of policy restrictions on outbound messages to ensure that they will conform to the receivers’ published policy. And even better, WSE will attempt to automatically retrieve the necessary security tokens and do the work of signing and encrypting the message to bring it into compliance to the outbound policy. This means that a well planned use of policy with WSE can greatly reduce the amount of code that you need to write, and by using policy, you can change security requirements for your WSE-based application on the fly without having to modify and recompile your code.
In both inbound and outbound cases, policy enforcement is implemented by a set of policy input and output filters. The policy verification input filter ensures that incoming messages conform to policy assertions registered for the requested resource. If an incoming message does not conform to these requirements, a PolicyValidationException is thrown and a message containing a Fault element in the body is returned to the sender with an error message indicating that the request did not conform to the policy. Conversely, the WSE policy- enforcement output filter also ensures that all outgoing messages conform to any send policies defined for outbound messages. If the SoapContext object of an outbound message doesn’t already have the elements needed to conform to the send policy, this filter attempts to retrieve security tokens from a security token cache and sign or encrypt the message to bring it into conformance with the policy. If a send-policy violation is detected in an outbound message, a PolicyEnforcementException exception is thrown.
The policy documents used by WSE when testing messages for compliance are stored at the local computer in an XML policy cache file. While typically stored in the directory with the application, policy cache files can be stored in any directory to which the WSE runtime, under the ASP.NET worker process account, has access. The policy cache document is made up of one or more policy assertions, as specified by WS-Policy, and a mapping between policies and the resources against which the policies are being verified, wherein policies are mapped to resources using the Id policy attribute. The following is an example of a valid WSE policy cache document:
wsse:X509v3 WSE Test Certificate wsp:Body() X509v3 WSE Test Certificate wsp:Body()
Version 2.0 of WSE implements support for both Confidentiality (encryption) and Integrity (signing) assertions out of the box. To use the policy filters to validate or enforce any other policy assertions defined by WS-PolicyAssertions or WS-SecurityPolicy, you will need to use the policy API implemented in the Microsoft.Web.Services.Security.Policy and Microsoft.Web.Services.Policy namespaces to write your own assertions handler. After writing the handler, you must then register it with the policy filters. As this is an advanced scenario, I won’t be covering it here. Instead, you should refer to the WSE 2.0 SDK documentation.
In theory, the WSE policy filters should be able to successfully parse any policy assertions that conform to the WS-Policy, WS-PolicyAssertions, and WS- SecurityPolicy specifications. However, to ensure that a policy cache document can be successfully parsed and interpreted by WSE, you should either use the WSE Settings Tool to create a basic policy cache document or possibly start with a WSE-generated document and add your own assertions and operators. In the next section, I will show how to use the WSE Settings Tool to generate a simple policy cache document.
In version 2.0, WSE doesn’t support any of the discovery mechanisms for policies described in WS-PolicyAttachment. To make policies visible to applications that consume your Web service, you must either send a copy of a policy document to your Web service’s consumers or present an accessible URL location from which the policy document can be accessed. An even better way to make policies discoverable would be to manually edit the WSDL file, following the rules prescribed in WS-PolicyAttachment that I discussed earlier. Then you must publish both the WSDL and the policy document file so that applications can access these discovery documents. When publishing a policy document, it would be nice to be able to digitally sign it, as is recommended by WS-Policy. However, in this release, WSE doesn’t provide an easy way to apply the WS- Security features to a policy cache document. Remember that as it stands today, WSDL consuming tools, such as the Visual Studio .NET Add Web Reference tool, will not be about to automatically consume these policy extensions to your WSDL files and be able to find the referenced policies,
Why We Need Policies
It’s all very well that we now have specifications such as WS-Security and WS- Attachments that prescribe how to handle tasks necessary for SOAP messaging, but those specifications don’t really answer the question of how we expose these functionalities to our Web service consuming clients. We have seen in previous chapters that a Web service request can contain a set of credentials that can be used by the requested service to determine whether to allow access. That’s great, but how do you know which credentials to include in the request message? Should you send along a Kerberos ticket, and if that doesn’t work, try a X.509 certificate, or maybe load the message down with every possible security token that you can assemble? Although a message loaded down with WS- Security-defined elements might be more secure, it's inefficient to perform the extra work of adding these security elements when the recipient doesn't require them and possibly can't even handle such a message.
At this point, I know that many of you have just developed—in your minds—some sort of service, which for fun you might have named MyInfoService, that can be called in advance of making a request to the service that you need to access, say MyDataService, in order to get the applicable policy requirements. MyInfoService might tell you which security tokens to send to MyDataService, which languages are supported, what the message encoding should be, and the like. After this neat epiphany, you might then start to think about the difficulties of late binding to such a service, the extra logic that will need to be built into the client to assemble the right information at run time to make the request to MyDataService. After thinking more about the costs of making an extra SOAP request to MyInfoService before calling MyDataService, you might begin to pine for the early-binding benefits of consuming a WSDL file at design time.
Limitations of Existing Description Mechanisms
As we discussed in Chapter 1, one of the major benefits of WSDL is that it lets us consume the description of a Web service at design time so that we know, at run time, how to interact with the service. Unfortunately, although WSDL is designed to describe items such as exposed Web methods, data types, and SOAP message elements, it has proven less useful when it comes to describing choices, preferences, and other requirements not tied directly to the messaging interface.
For example, WSDL can tell you that MyDataService expects a SOAP request message to have a body element, which contains a certain child element, which corresponds to an exposed Web method. It can also provide schema information for this element. Each exposed Web method will have this information, and there will be similar information about what will be returned should the methods execute successfully. WSDL can even tell you that certain header elements are required, such as those that might provide authentication information or otherwise identify the requester or a particular message. Unfortunately, WSDL alone can’t help us in the case of conditional requirements and other policy issues not directly related to either message transport or describing a SOAP message. Fortunately, WSDL does provide the extensibility to support the definition of policy requirements, as we will discuss shortly.
Defining Web Service Requirements
Up to this point, I have taken a somewhat compartmentalized approach to my discussion of Web service specifications and their related functionalities. This is in part for clarity’s sake but also because the modular design of these specifications enables us to treat them in this manner, implementing the ones that we feel are necessary for our application and simply forgetting the rest. However, when we get into real-world scenarios, things can and will get more complicated. It’s certainly not unanticipated that, in an enterprise Web service, we’ll be carrying out operations such as encrypting and signing SOAP messages, transmitting security tokens, and providing custom routing information. Of course, as I have pointed out, the real question when accessing Web services is not, “How do I do these things?” since the specifications tell us how, but rather, “Which of these must I specify for my request message to successfully return me my ‘Hello World?’” Or, “How can I tell the customers of my Web service what they need to do to get their ‘Hello World?’” Alternatively, how can customers tell my Web service about the messaging requirements of their client applications so that messages can also be received by the clients?
The answer is that we need a way for communicating the full breadth of what our Web service requires of its clients, which can be thought of as our Web service’s policy statement. The same goes for communicating the client’s requirements to the service. Since both parties in a Web service-based interaction need to be able to enforce their own policies and interpret other policy statements, it really makes more sense to discuss policy in terms of sender and receive rather than client and service.
The policy-related specifications, backed by IBM, Microsoft, BEA Systems, SAP and others, officially define policy as “the collection of all acceptable policy assertions for a specific domain” (WS-Policy Section 3.4). In this definition, an assertion is considered a requirement or preference of the service. The following are examples of some assertions that can be made by a Web service:
You must send either an X.509 certificate from you or else a Kerberos ticket, and Kerberos tickets are preferred.
You must encrypt your message header.
You must sign the message.
You must send requests only in either German or Dutch.
You can send messages encoded in either UTF-8 or ANSI_X3.4-1968.
In the rest of this chapter, I’ll discuss the specifications that describe how to declare such policies for our Web services. Like all of the specifications that I discuss in this book, a main goal of these specifications is to be modular, wherein each layer is broken down into a separate specification, which includes the policy specifications described next.
WS-Policy
This specification describes a framework for defining and communicating the expectations and requirements both for sending messages to and receiving messages from a Web service, and it defines the format of such requirements.
WS-PolicyAssertion
This specification defines a core set of Web service-policy assertions dealing mostly with messaging issues, encoding, and other requirements that fall outside WSDL.
WS-SecurityPolicy
This specification describes a set of policy assertions that defines the types of security features that a Web service has implemented and the type of security that’s required of an incoming request message. Although WS-SecurityPolicy is often considered a security specification, I discuss it in this chapter because it does define policy assertions regarding Web services security and leverages the WS-Policy framework for describing assertions.
WS-PolicyAttachment
This specification, in essence, ties everything together by defining how policy expressions are linked to WSDL and Universal Description, Discovery, and Integration (UDDI) as well as to other discoverability mechanisms.
Not only are the above specifications designed, to leverage one another, but they are also orthogonal to one another. This means that, for example, the security policy assertions defined in WS-SecurityPolicy can be taken advantage of in a framework other than the one described in WS-Policy or even just passed around in SOAP message headers. In subsequent sections, I will delve into what these specifications describe in a bit more detail, and we will see how WSE makes it work. I do cover UDDI-specific policy bindings in this book.
Web Services Policy Framework
As the highest level of these policy-related specifications, the WS-Policy specification describes the framework that can be used to combine various policy assertions, each one of them an XML node, into a single policy expression. Applications and services can then use this policy expression to determine the requirements for accessing a Web service provider, and a policy expression might apply to one or more Web methods supported by a Web service or even be used with multiple Web services across an entire domain. As such, multiple policy expressions can be combined into an overall policy document for an entire entity, such as a domain or a company.
Although they might sound complicated, policy expressions are simply hierarchical structures of XML nodes, in which these nodes are either policy assertions, policy operators, or other policy expressions. The structure of a policy expression conveys the ultimate requirements for accessing the service. For example, you can create a policy expression that says a valid request message must have property A, and if not, it must have properties B and C.
The following is an example of a simple policy expression that makes two security assertions:
In this statement, the two assertions are that a request message must include a valid X.509 certificate that can be used to identify the sender and that the message must be digitally signed using the Advanced Encryption Standard (AES) algorithm so that the recipient can determine whether the message has been tampered with. As you can see, a policy statement is simply an XML structure that combines one or more assertions using policy expressions.
Policy Operators
Much like logical or arithmetic operators, policy operators allow us to define relationships among multiple assertions in a policy expression. WS-Policy defines the following operators, which themselves can contain other nested operators.
All
This element dictates that all assertions or other expression elements that are child elements must be satisfied in order to access a Web service. In the following example, All indicates that an X.509 certificate must be included in the request and that the message must be encrypted and digitally signed:
ExactlyOne
This element dictates that only one of the requirements prescribed by the contained assertions is to be followed when the service is accessed. For example, the following policy expression states that the requesting application must provide only one of three types of security tokens for authentication:
Note that when the ExactlyOne operator is used, it overrides the Usage attribute value, which in this example is Required. When using the ExactlyOne operator, it’s helpful to also specify the optional Preference attribute on the contained assertions to give requesting applications an idea of which of these assertions are preferred. A higher Preference value is more preferable than a lower value.
OneOrMore
This element dictates that at least one of the requirements prescribed by the contained assertions must be followed when accessing the service. For example, the following policy expression states that the requesting application need provide only one of the three types of security tokens for authentication but can provide more if desired:
Like ExactlyOne, the OneOrMore operator benefits from having the Preference attribute specified on the contained assertions.
PolicyReference
Although not technically a policy operator, the PolicyReference element provides a means whereby one policy expression can be included, by reference, as an element in another. This element is treated as an All element with respect to the assertions and operators in the policy expression that references it. For a policy expression to be referenced, it must have a Uniform Resource Identifier (URI) assigned by the Id attribute or else a qualified name assigned to the Name attribute. The following is an example of a policy expression that references two other expressions, one by name and one by URI:
Policy Assertions
Assertions are the building blocks of a policy expression, and they describe the rules that an application must follow when making requests to a Web service. Policy assertions follow this general XML format:
General Messaging Assertions
The WS-PolicyAssertions specifications define a set of policy assertions that apply to the general messaging behaviors of SOAP-based Web services. All of these assertions follow the general format defined in WS-Policy and are defined next.
TextEncoding
If a Web service supports more than one character encoding or requires a nonstandard encoding, the TextEncoding assertion can be used to define such a policy, provided the XML specification supports the encoding. Of course, to maximize interoperability, the default encoding of UTF-8 should be used. The following sample is a policy expression that declares an option between two supported character encodings:
Language
If a Web service supports multiple languages for textual content or if English isn’t the standard, the Language assertion can be used to declare such policies. The following policy example declares that both English and German are supported:
SpecVersion
This assertion enables a Web service to specify which versions of a specification request messages must conform to, where a specification is referenced by its URI.
MessagePredicate
This assertion is used to ensure that request messages contain the necessary XML elements in order to comply with a Web service’s requirements. This assertion is particularly useful for declaring requirements for proprietary elements in the message header or body elements. The message predicate is selected using either XPath 1.0 or the message part selection functions defined in WS-Policy, which are used to select either the entire message body (wsp:Body) or a specified message header (wsp:Header). The selection language used is specified in the Dialect attribute; if this attribute is not specified, the default, XPath 1.0, is assumed. While the two message part functions are easier to use, XPath should be used to select specific parts of a body or header element. The WS-PolicyAssertions specification introduces nine new XPath functions for selecting parts of a SOAP message, which I won’t detail here. In the following assertion, an XPath expression, which uses the new GetHeader function, identifies messages with only one MyCustomHeader header element:
Note that the use of XPath expressions to select the message predicate is not supported by WSE 2.0.
Security Policy Assertions
While there is certainly a need for general messaging assertions, the drive to declare security requirements to enable interoperability with the WS-Security specification was certainly a major impetus behind the development of WS-Policy. Thus an entire specification, the WS-SecurityPolicy specification, is devoted to defining security assertions. These assertions, most of which are supported by WSE 2.0, are defined next.
SecurityToken
This assertion is used to declare which types of security tokens, defined in WS-Security, must be supplied in order to authenticate a requesting application. Usage of the security tokens listed in Table 6-1 can be specified by this assertion.
Table 6-1: Security Tokens with Defined Policy Assertions Qualified Name
Token Type
wsse:X509v3
X.509 Certificate
wsse:Kerberosv5TGT
Kerberos ticket-granting ticket (TGT)
wsse:Kerberosv5ST
Kerberos service ticket
wsse:UsernameToken
Username token
wsse:SAMLAssertion
SAML Assertion
wsse:XrMLLicense
XrML License
In this table, SAML refers to an XML-based security-assertion token.
WS-SecurityPolicy supports additional optional elements for specifying additional information about token requirements, including which certificate authorities and key distribution centers are acceptable and any token-specific information that makes it easier for requesting clients to present the correct token in the request message. For example, the following policy expression defines acceptable tokens as X.509 certificates from one of two different certificate authorities: a Kerberos TGT from the specified key distribution center or a username token with the specified username.
For more information about details and usage of security token assertions, refer to the WS-SecurityPolicy specification.
Integrity
When a Web service requires that incoming request messages be digitally signed to indicate whether they have been tampered with or modified, an Integrity assertion can be added to the policy expression. The following is an example of an assertion that the message header must be digitally signed with an X.509 certificate using the Secure Hash Algorithm (SHA) algorithm:
For more information about details and usage of integrity assertions, refer to the WS-SecurityPolicy specification.
Confidentiality
If the Web service requires that any parts of a request message be encrypted, this can be stated in the policy expression using a Confidentiality assertion. A Confidentiality assertion that declares that the EncryptedElement node in the message body must be encrypted using the service’s X.509 certificate and Triple-DES (Data Encryption Standard) specification might look like this:
Visibility
A request message to a Web service can be routed through one or more intermediate Web services. If any of these intermediaries need to be able to access part of the request message, a Visibility assertion can be made to prevent that portion of the message from being encrypted. The following example assertion requires that the message header not be encrypted:
SecurityHeader
This assertion specifies requirements of the format of the Security header element in a request message. The following example requires that child elements within the Security header be prefixed by the WS-Security XML namespace and that only encrypted elements specifically referenced in the Security header will be processed:
MessageAge
This assertion defines the maximum number of seconds a message has to travel from the sender to the receiver. The total travel time is determined by using the value of the Timestamp header, which makes it mandatory when this assertion is used. If the message transmission time exceeds the value of the Age attribute, the message is rejected. The following is an example of this assertion with an age of 30 minutes:
Obviously the smaller this value, the more important it becomes for the two endpoints to have their times synchronized. The means by which these machines synchronize their times is not addressed in any of the specifications addressed by this book. Rather, time synchronization is normally accomplished via some out-of-band mechanism like a phone call or use of a common time server.
Reliable Messaging Assertions
The WS-ReliableMessaging specification, which I described in Chapter 4, defines a set of policy assertions that are used when declaring the message reliability features that must be used when accessing a Web service endpoint. These assertions include the following:
Specification Version This assertion is used to declare the version of WS-ReliableMessaging to which a message must conform. This assertion is made by the SpecVersion element, as in the following:
Delivery Assurance This assertion is used by the Web service that is the ultimate recipient to declare message delivery behaviors when receiving messages from the service guaranteeing reliability. This assertion is made by the DeliveryAssurance element, where the Value attribute can be one of these values:
wsrm:AtMostOnce Messages are delivered only once.
wsrm:AtLeastOnce Messages are delivered at least once.
wsrm:ExactlyOnce Messages are delivered exactly once, which is equivalent to both of the previous assertions together.
wsrm:InOrder Messages are delivered in the order they were sent.
For example, the following assertions request that messages be delivered by the reliability service exactly once and in the order that they were sent:
Sequence Expiration This assertion declares the time at which a reliable messaging sequence is no longer valid. This assertion is made using the wsu:Expires element. The following assertion indicates that a sequence expires on December 31, 2005:
Inactivity Timeout This assertion declares the number of milliseconds before an inactive sequence expires. The Milliseconds attribute of the InactivityTimeout element is used to specify this inactivity period. The following assertion indicates that a sequence will expire if it is inactive for longer than 1 minute:
Retransmission Interval This assertion declares the number of milliseconds before an unacknowledged message is re-sent. The Milliseconds attribute of the BaseRetransmissionInterval element is used to specify this period. Another related assertion, ExponentialBackoff, is used to declare that a previously agreed upon algorithm is used when a new retransmission interval is negotiated. The following assertion indicates that a message must be re-sent if an acknowledgement has not been received within 1 minute and that the back- off algorithm should be used:
Acknowledgement Interval This assertion declares the number of milliseconds before an acknowledgement must be sent in a separate message. The Milliseconds attribute of the AcknowledgementInterval element is used to specify this period. The following assertion indicates that a stand-alone acknowledgement message must be sent if another message is not being returned to the sender within 1 minute:
Referencing Policy Expressions
Of course, it’s not enough that we can simply create policy expressions. Just as important are mechanisms for communicating policy expressions to other parties and for applying them against sent and received messages. In fitting with the Web service model promoted by WSDL, the best way to communicate policies are to make them discoverable and accessible to the parties who must access a Web service endpoint. We already have mechanisms defined for publishing Web service definitions, such as WSDL and UDDI. However, we need to be able to locate policy files and reference policy expressions from these description-and-discovery documents. The WS-PolicyAttachment specification provides mechanisms for including policy expressions within an XML document, such as a SOAP message header, or referencing them from an XML document, such as a WSDL file. By linking policies to WSDL, we can leverage the discoverability, description, and extensibility features of WSDL.
Before I discuss how to link policies to WSDL, I need to discuss some of the semantics for identifying policies. While policy expressions are the integral unit for declaring policies, when publishing multiple policy assertions, a service may want to store all policy assertions in a single document, even when these policies relate to multiple endpoints implemented by the service. WS-Policy provides a way to reference a single policy expression within an XML document containing multiple expressions using either a qualified name or a URI.
To reference a policy expression using a URI, the Id attribute must be supplied, as defined by the Web Service Security Addendum specification. This enables the policy expression to be referenced relative to a base URI plus the pound sign and the name assigned in the Id attribute. For example, you can reference the following policy expression in the http://example.com/policy namespace as http://example.com/policy#MySecurityPolicy:
⋮
⋮
The other mechanism for referencing a policy element involves assigning a qualified name to the policy expression, which is done using the Name attribute as defined by WS-Policy. When the Name attribute is used to specify policy expressions, an expression named MyTextEncodingPolicy in the http://example.com/policy namespace is referenced as p:MyTextEncodingPolicy, where xmlns:p=”http://example.com/policy”.
To hook up policy expressions to other XML documents, the WS-PolicyAttachment specification defines two mechanisms for referencing policy expressions. The first enables a policy expression to be referenced within any arbitrary XML element. This mechanism lets us add a PolicyURIs attribute to any XML element and reference the URI for a policy expression. Likewise, the PolicyRefs attribute can be added to any XML element to reference a policy expression by its fully qualified name. The following example element references these policy expressions that I have just discussed:
The other mechanism enables a policy expression to be assigned to an arbitrary Web service resource; such resources can be specified using the semantics of the WS-Addressing specification, which I discussed in Chapter 4. Known as a policy attachment, this XML policy structure is composed of the following elements:
A reference to the resource to which the policy statement applies, where this reference is based on WS-Addressing.
A policy expression either defined by a Policy element or referenced by a PolicyReference element.
Security information, defined using WS-Security, such as a digital signature.
In the following example, the PolicyAttachment element maps two policy expressions that reference Web services, wherein the security token policy is contained within the policy attachment document and the message age policy is referenced externally:
Policy Assertions and WSDL
Now that you have the means to define policies for particular Web service resources, you need to add this information to your WSDL files so that it can be publicly exposed to applications that consume your services. The ideal method for publishing a policy assertion for a given resource is to add it to the part of the WSDL file that defines the section of the Web service to which policy applies. However, when doing so, it is important to remember that policy assertions made on parent WSDL elements are enforced on child elements, which means that we must use care when applying policies at multiple, overlapping levels in a service definition because we may get unexpected results for the effective policy for a given WSDL element. For example, when a policy assertion, say a signature requirement, is defined at the message level of the service description, this policy will apply to all parts of the message. However, when a policy, such as confidentially requirement, is also defined for a single message part, the effective policy for that part of the message will include both the signature policy, defined for message, and the confidentiality policy, defined on the part. This can get even more complicated when you define a policy for the portType or operation elements to which the message belongs. In such cases, the effective policy for a message part is determined as the sum total of the policies defined for the portType, operation, message, and part elements.
To make things even less straight-forward, because of restrictions in WSDL 1.1, you aren’t able to add arbitrary XML, such as a PolicyAttachment element, to portType and message elements. Instead, WS-PolicyAttachment specifies that a required UsingPolicy element be added to the WSDL definition to indicate that policies are being defined. In addition, policy references are added to the appropriate elements in the WSDL document for the Web service functionalities for which policies are being defined. When consuming a policy annotated WSDL file, you must be able to retrieve the externally referenced policy files. For example, the following WSDL file includes policy information for the DocumentService Web service:
⋮
In this example, any requests to the GetDocument method must contain a X509-based security token, and all parts of the request message to the GetDocument Web method must conform to a signature policy referenced by the SignaturePolicy identifier. In addition, the body of the request message must conform to the encryption policy referenced by the EncryptionPolicy identifier. This makes the effective policy for the body of a request message to the GetDocument method the same as the following:
Securing Policy Expressions
As publicly exposed in XML documents, policy expressions and policy attachments are exposed to the same kinds of vulnerabilities as SOAP messages being transported using an unencrypted transport; therefore, policy expressions require the same kind of security considerations that are given to SOAP messages. For example, if I’m consuming a policy expression for a Web service, I need to be able to know that this policy expression has indeed been published from the Web service that I’m expecting and hasn’t been modified by any malicious parties. To ensure this, the Web service must digitally sign its policy expressions using a trusted security token (either a shared secret or a private key) as defined in WS-Security. In a similar manner, if I need to protect my Web service’s policies but also make them publicly available, I can encrypt parts of the policy expression using XML encryption as defined in WS-Security.
Because you can apply the features of WS-Security to a policy expression as you would a SOAP message, WS-Policy supports including the Security element, as defined by WS-Security, within a policy expression. When defined in this way, the Security element specifies the security features that are implemented in the policy expression. For example, the following policy expression includes a digital signature, which is defined in the Security element:
In this example, the NULL value of the URI attribute in the Reference element indicates that the entire expression will be included in the signature. However, the act of signing will change the Signature element and, thereby, the contents of the expression, so I specify the referenced XPath transformation to indicate that the digital signature doesn’t include the Signature element.
Policy Support in WSE
Version 2.0 of WSE includes the ability to declare and enforce WS-Policy– based restrictions on Web services that are enabled to use WSE. While WS- Policy implies that policy is something that applies only to inbound messages, WSE has been designed to apply WS-Policy functionality to outbound messages as well. This enables you to place the same type of policy restrictions on outbound messages to ensure that they will conform to the receivers’ published policy. And even better, WSE will attempt to automatically retrieve the necessary security tokens and do the work of signing and encrypting the message to bring it into compliance to the outbound policy. This means that a well planned use of policy with WSE can greatly reduce the amount of code that you need to write, and by using policy, you can change security requirements for your WSE-based application on the fly without having to modify and recompile your code.
In both inbound and outbound cases, policy enforcement is implemented by a set of policy input and output filters. The policy verification input filter ensures that incoming messages conform to policy assertions registered for the requested resource. If an incoming message does not conform to these requirements, a PolicyValidationException is thrown and a message containing a Fault element in the body is returned to the sender with an error message indicating that the request did not conform to the policy. Conversely, the WSE policy- enforcement output filter also ensures that all outgoing messages conform to any send policies defined for outbound messages. If the SoapContext object of an outbound message doesn’t already have the elements needed to conform to the send policy, this filter attempts to retrieve security tokens from a security token cache and sign or encrypt the message to bring it into conformance with the policy. If a send-policy violation is detected in an outbound message, a PolicyEnforcementException exception is thrown.
The policy documents used by WSE when testing messages for compliance are stored at the local computer in an XML policy cache file. While typically stored in the directory with the application, policy cache files can be stored in any directory to which the WSE runtime, under the ASP.NET worker process account, has access. The policy cache document is made up of one or more policy assertions, as specified by WS-Policy, and a mapping between policies and the resources against which the policies are being verified, wherein policies are mapped to resources using the Id policy attribute. The following is an example of a valid WSE policy cache document:
Version 2.0 of WSE implements support for both Confidentiality (encryption) and Integrity (signing) assertions out of the box. To use the policy filters to validate or enforce any other policy assertions defined by WS-PolicyAssertions or WS-SecurityPolicy, you will need to use the policy API implemented in the Microsoft.Web.Services.Security.Policy and Microsoft.Web.Services.Policy namespaces to write your own assertions handler. After writing the handler, you must then register it with the policy filters. As this is an advanced scenario, I won’t be covering it here. Instead, you should refer to the WSE 2.0 SDK documentation.
In theory, the WSE policy filters should be able to successfully parse any policy assertions that conform to the WS-Policy, WS-PolicyAssertions, and WS- SecurityPolicy specifications. However, to ensure that a policy cache document can be successfully parsed and interpreted by WSE, you should either use the WSE Settings Tool to create a basic policy cache document or possibly start with a WSE-generated document and add your own assertions and operators. In the next section, I will show how to use the WSE Settings Tool to generate a simple policy cache document.
In version 2.0, WSE doesn’t support any of the discovery mechanisms for policies described in WS-PolicyAttachment. To make policies visible to applications that consume your Web service, you must either send a copy of a policy document to your Web service’s consumers or present an accessible URL location from which the policy document can be accessed. An even better way to make policies discoverable would be to manually edit the WSDL file, following the rules prescribed in WS-PolicyAttachment that I discussed earlier. Then you must publish both the WSDL and the policy document file so that applications can access these discovery documents. When publishing a policy document, it would be nice to be able to digitally sign it, as is recommended by WS-Policy. However, in this release, WSE doesn’t provide an easy way to apply the WS- Security features to a policy cache document. Remember that as it stands today, WSDL consuming tools, such as the Visual Studio .NET Add Web Reference tool, will not be about to automatically consume these policy extensions to your WSDL files and be able to find the referenced policies,
Reliable Messaging for Web Services
Although SOAP and its underlying transport protocols provide adequate support for sending a message from point A to point B, Web services require a more robust mechanism to guarantee that messages are being received by their intended recipients and that message addressing remains consistent, even when messages are routed or carried using heterogeneous message transports.
Introduction to Reliable Messaging
SOAP is a high-level protocol in the overall Web services protocol stack whose main job is to provide a package for XML data, the metadata needed by the recipient, and a binding to a transport. Therefore, SOAP relies on lower-level protocols, such as HTTP, SMTP, and TCP to get the SOAP message and its data payload from endpoint to endpoint. When you send a SOAP message using TCP, HTTP, or any other potential transport protocol for SOAP, these transports see the message path only as a pair of endpoints. Unlike the protocols to which they’re bound, SOAP messages need a more holistic view of where they’re headed. Certain Web service implementations may require a SOAP message be modified by a specific sequence of Web servers before reaching its ultimate destination. A Web service may also need to be able to delegate the processing of an incoming message to other services. There might even be a need during this message routing process for a SOAP message to be transferred from one transport to another, say from HTTP to TCP. Of course, the core SOAP specification doesn’t provide a mechanism for reliably addressing SOAP messages through this kind of complex message routing.
While you generally think of Web services using a two-way, request-response messaging behavior, Web services also support other types of messaging behaviors, including asynchronous messaging. The Web Services Description Language specification (WSDL) identifies the following Web service messaging behaviors:
Request-response in this synchronous messaging scenario, the client sends a request message to the Web Service and receives response message back from the service.
One-way a type of asynchronous messaging, the client sends a message, but a response message is not required or expected. This might be used when a client needs to send period status updates to a Web service.
Solicit-response The Web Service sends a message to the client, and the client then sends back a correlated reply message. This might be used when a Web service needs to poll clients for status information.
Notification another type of asynchronous messaging; the Web Service sends a message to one or more clients without requiring a response. This might be used for clients who have subscribed to event notifications from a Web service.
An equally important concern in SOAP messaging is the issue of message reliability. If we cannot guarantee that messages will be transmitted reliably between endpoints, all of the expected benefits of Web services cannot be realized. Since we do live in a world of unreliable networks, there needs to be a mechanism to enable a message sender to verify that one or a series of SOAP messages have indeed reached the intended recipient. Therefore, reliability for SOAP messaging must address the following issues:
Message ordering The need to order a series of related messages regardless of when they actually arrive.
Message acknowledgment The need to be able to send a response back to the sender stating that one or more messages in a series have been received.
Message delivery policies The need for an endpoint to be able to request specific reliability behaviors for requests, such as the number of times that a failed message should be re-sent and how long to wait before resending.
is transport independent, and WS-ReliableMessaging, which defines mechanisms for message acknowledgment and ordering and for declaring messaging policies.
in real- world applications, your Web service might need to implement a more complex message-routing scenario, such as dynamic, decision-based routing; routing through firewalls or intermediaries; or even using multiple transport protocols. For example, consider a Web service application that has been scaled out to run on multiple Web servers and behind a firewall. In such a scenario, a load- balancing application could be used to route request messages to whichever Web server was best able to handle the load. For example, such an application might determine, based on relative loads, whether to route a request to a processing center located on the East Coast or one located on the West Coast. In a multiple-message transaction, it would then be important that all future requests be sent directly to the application center where the original request was being handled. If this level of specificity for addressing is not supported in the message itself, then the routing application would need to do extra work to determine which processing center should handle all subsequent incoming messages from the client and would likely require the expense of having to maintain a server-side process that includes state information.
In a scenario like this, it is important to have an addressing mechanism that transcends the simple point-to-point functionality provided by the message transports. Among other benefits, such a mechanism would enable any intermediary Web services, such as routers, to have all of the addressing information that they need to ensure that the messages end up in the right place. To provide this consistent addressing for Web services, the WS-Addressing specification defines two constructs for conveying message addressing information: endpoint references and message headers. In this definition, an endpoint is simply an addressable resource to which a SOAP message can be sent; the resource can be a Web service or client application, a SOAP router, or any other SOAP-aware entity.
Addressing Endpoint References
In the reliable messaging model, SOAP messages are sent between and via endpoints. To address a messaging endpoint, we need to be able to explicitly, uniquely, and consistently reference such endpoints. As an additional hurdle, endpoint references must be discoverable so that messages can be addressed dynamically. Of course, as with Web Services Description Language (WSDL), before an endpoint reference can be consumed it must first be described. WS- Addressing introduces a mechanism for describing endpoint references.
From a WSDL standpoint, endpoint description information would most logically be defined within the Service element of a WSDL document; however, WSDL 1.1 does not allow for the extensibility of this element. To get around this limitation, WS-Addressing defines a new typed EndpointReference element that can be used within a Web service description document, such as WSDL, to describe a referenced endpoint. The EndpointReference element supports the following child elements:
Address Provides the Uniform Resource Identifier (URI) of the endpoint, which can be a network or logical address.
ReferenceProperties Provides application-specific information that must be supplied when addressing messages to a particular endpoint.
PortType Specifies the WSDL port type of the referenced endpoint.
ServiceName Specifies the WSDL service that defines the referenced endpoint. This element can also include a PortName attribute that references the name of the WSDL port definition for the endpoint.
Policy Can contain one or more policy assertions, which can be used to describe the policies that are enforced at an endpoint.
The following is an example of an endpoint reference for the CollectionService Web service:
http://example.com/collectionservice/collectionservice.asmx x:CollectionServiceSoap x:CollectionService ⋮
In this example, the Policy element would contain a policy assertion element, which I’ll describe along with a detailed discussion of Web service policies in Chapter 6. By adding this endpoint reference to the WSDL file that describes the CollectionService Web service, you can dynamically assign a policy behavior to the endpoint. In addition to being used in description documentation, the EndpointReference element is also the base type used when describing an endpoint in one of the SOAP message headers defined by WS- Addressing.
Addressing Message Headers
In addition to defining a mechanism for referencing message endpoints, WS- Addressing also specifies a set of message headers that can be used during transport to define the endpoints on the message path. When endpoint information is transported in SOAP headers, this information can be accessed by messaging intermediaries when they need to make intelligent routing decisions. The following SOAP message header elements, which are child elements of the Header element, are defined by WS-Addressing:
To Contains information about the immediate message recipient in the form of a URI.
From Contains information about the message sender in the form of the endpoint reference type.
ReplyTo Contains information about the endpoint to which a reply message should be sent in the form of the endpoint reference type.
Recipient When a message is being routed via one or more intermediaries, can be used to specify an intended ultimate message recipient and is in the form of an endpoint reference type.
FaultTo Contains information about the endpoint to which any fault response messages generated by the request should be sent, in the form of the endpoint reference type.
Action A URI that represents the action for the message, which can be the same as the value of SOAPAction in an HTTP message header.
MessageID A URI that is used to uniquely identify a message.
RelatesTo Used to relate a message to another message by relationship type. This optional element contains a URI that is the MessageID value of the related message. The relationship type is specified using the element’s RelationshipType attribute, where at this point the only accepted value is Response.
The following SOAP request message to the DocumentService shows the use of WS-Addressing message headers.
http://example.com/documentservice/GetDocument http://schemas.xmlsoap.org/ws/2003/03/addressing/role/anonymous uuid:0a23b19a-ca6c-40be-b6a0-56b8f22c7df3 http://localhost/documentservice/documentservice.asmx ⋮
⋮
In this example, the URI value in the From element that defines the endpoint of the requesting party is http://schemas.xmlsoap.org/ws/2003/03/addressing/role/anonymous . This URI is used when a fixed URI cannot be assigned to the Address value of an endpoint. Of course, this anonymous URI value cannot be used with the To element, and if the value is specified in the ReplyTo or FaultTo element, there must be an alternative mechanism for communicating this information.
WSE supports the headers defined in WS-Addressing and, by default, generates a set of addressing headers for all outbound SOAP messages. Also, when incoming messages contain addressing headers, the WSE input filters read these headers and generate the relevant programming objects. As I’ll show later in this chapter, the WSE API can be used to add or change the values of these message headers or to access the addressing information for incoming messages.
Message Reliability for Web Services
While WS-Addressing helps to solve the challenges of consistently describing and accessing Web service endpoints, the general issues of getting messages reliably between message endpoints are being addressed in another specification. A Web service needs to be able to determine when messaging has become unreliable so that it can adjust accordingly, which might involve resending certain messages or delaying the processing of messages until other messages arrive. To that end, IBM, BEA Systems, and Microsoft have again teamed up to draft WS-ReliableMessaging, a specification that attempts to guarantee the reliability of SOAP messaging by defining message acknowledgments, message ordering, and reliability policies
Message Ordering
When a Web services–based interaction comprises a series of SOAP messages, it’s conceivable that because of transmission on unreliable networks these messages might not be received in the order in which they were sent. When messages in a series are lost or delayed, the interaction can become unreliable. This can cause problems that require advanced application logic for handling cases when the message sender assumes that as a result of an earlier message the Web service is in a certain state, but when messages are received out of order, this might not be the case.
To track the order of Web service requests, WS-ReliableMessaging introduces the Sequence SOAP header element. When a client communicates with a Web service implementing WS-ReliableMessaging, the Sequence element is used to number the messages in a sequence, set a time-out period for a sequence, and to declare the end of a sequence. The Sequence element implements the following child elements:
Identifier A unique identifier for the sequence, which leverages the Identifier element defined in the Web Service Security Addendum specification and which is qualified against the http://schemas.xmlsoap.org/ws/2002/07/utility namespace.
MessageNumber A value that defines the sequence number for the message, wherein numbers are assigned as consecutive unsigned integers starting at 1 and increasing by 1 throughout the lifetime of the conversation.
LastMessage Declares that a message is the last in the sequence.
Expires Declares a time in the future when the sequence expires, whether or not the LastMessage element has been used.
The following is an example of a message containing the Sequence header; this message is the third in a sequence, which is defined by the GUID in the Identifier element:
⋮
uuid:56b8f19a-40be-ca6c-b6a0-89eaf22c7df3 3 ⋮
⋮
Message Receipt Acknowledgments
Since the Web service architecture enables asynchronous messaging, there is no requirement that a response message be returned for any given request message, as there would be in two-way messaging. However, from the standpoint of message reliability, such an acknowledgment in essence closes the loop on the request process. In asynchronous messaging and without receipt acknowledgments, the requesting party has no way of knowing whether the request message was received. In this case, if there was unreliability on the network, it will likely be manifested later in the interaction as failures that result from having both parties in a different application state. For example, an application might send a one-way request to open a “shopping cart” session, with items to be added in later requests. If this request was not received, subsequent requests to add items to the “cart” will fail since the Web service never received the first request to create the cart session.
One solution might be simply to return a response message that a new cart session was created, essentially making it a two-way messaging scenario. While this dedicated acknowledgment message solves this reliability issue, it is inefficient to generate such a response message, particularly in situations in which both parties are regularly exchanging messages. In such cases, it would be more efficient to piggyback the acknowledgment in the header of an otherwise unrelated message. WS-ReliableMessaging proposes the use of a SequenceAcknowledgment header element that is used to return a receipt acknowledgment for one or more messages in a given sequence, either in an arbitrary response message or in a response created exclusively to return the acknowledgment. A SequenceAcknowledgment element supports the following child elements:
Identifier A unique identifier for the sequence, which leverages the Identifier element defined in the Web Service Security Addendum specification and is qualified against the http://schemas.xmlsoap.org/ws/2002/07/utility namespace.
AcknowledgmentRange Defines a range of one or more sequence numbers that identifies messages that have been received, where the range is defined by the integer values of the Upper and Lower attributes.
An acknowledgment for a single message in a sequence will have the same sequence number for both Upper and Lower attributes. If the range of messages in the sequence being acknowledged is not contiguous, more than one AcknowledgmentRange element can be used. The following example shows the inclusion of a SequenceAcknowledgment element in an acknowledgment response message:
⋮
uuid:56b8f19a-40be-ca6c-b6a0-89eaf22c7df3 ⋮
This acknowledgment indicates that message number 3 in the sequence was not received and that the requester should resend this message.
A Web service can wait for a negotiated amount of time before returning an acknowledgment, which gives it time to attach it to another outgoing message. However, once this limit has been reached, the service must generate a response message solely to return the acknowledgment, as in the preceding example. Of course, at any point the AckRequested header element can be sent with any message to the Web service to explicitly request that an acknowledgment for the sequence be returned. This element supports the following child element:
IdentifierA unique identifier for the sequence, which leverages the Identifier element defined in the Web Service Security Addendum specification and which is qualified against the http://schemas.xmlsoap.org/ws/2002/07/utility namespace.
The following message includes a request for a series acknowledgment:
⋮
uuid:56b8f19a-40be-ca6c-b6a0-89eaf22c7df3 ⋮
⋮
Reliable Messaging Policies
In addition to the functionalities just discussed that use reliability headers, WS- ReliableMessaging also defines a set of policy assertions that can be published by a Web service to declare which reliable messaging functionalities are required when accessing the endpoint. These policy assertions include the following general categories:
Specification version The version of WS-ReliableMessaging supported by the endpoint.
Delivery assurance The number of times a message can be delivered and whether message sequences must be delivered in order.
Sequence expiration The lifetime of a messaging sequence.
Inactivity time-out The time-out period after which a sequence is considered terminated.
Retransmission interval The time that an application should wait for an acknowledgment before resending the message.
Acknowledgment interval The time that a Web service should wait for a response message to be sent back to the requester before generating a stand-alone acknowledgment message.
WSE Support for Reliable Messaging
In version 2.0, WSE supports the WS-Addressing specification, but WS-ReliableMessaging is not yet supported. WSE support for WS-Addressing consists of a set of input and output message filters that read and write the SOAP headers defined in WS-Addressing. WSE automatically adds the appropriate addressing headers to all outgoing SOAP messages. In addition, WSE provides a full API for programmatically creating and accessing addressing objects, and you can explicitly define the addressing objects for outgoing messages to override the values that are automatically generated by WSE.
Creating Custom Address Headers
One of the things that you can do with WSE is to explicitly define the ReplyTo and FaultTo headers in an outbound message. This lets you redirect this type of response message to a different endpoint than the one from which the request was originally sent. When using the WSE, this can be accomplished programmatically by instantiating both a ReplyTo and a FaultTo object in the Microsoft.Web.Services.Addressing namespace, defining appropriate address URI values for each, and adding them to the SoapContext for the outgoing message, as in the following example:
// Create a new SoapContext for the request messageSoapContext myContext = myService.RequestSoapContext;
// Define a different reply address for the requestReplyTo replyAddress = new ReplyTo(new Uri("http://example.com/DocumentService/ResponseHandler.asmx"));
// Define a reply address for fault messagesFaultTo faultAddress = new FaultTo(new Uri("http://example.com/DocumentService/FaultHandler.asmx"));
// Add the custom ReplyTo and FaultTo addressing headers to the SoapContext myContext.Addressing.ReplyTo = replyAddress;myContext.Addressing.FaultTo = faultAddress;
// Security headers added here
// Call the GetDocument methodmyService.GetDocument(docID);
Based on this code, WSE generates the following SOAP request message:
http://example.com/documentservice/GetDocument http://example.com/DocumentService/FaultHandler.asmx http://schemas.xmlsoap.org/ws/2003/03/addressing/role/anonymous uuid:127ce5d7-8d06-4a02-b764-73fe61a8ab62 http://example.com/DocumentService/ResponseHandler.asmx http://localhost/documentservice/documentservice.asmx 2003-08-15T22:58:40Z 2003-08-15T23:03:40Z ⋮
⋮
In the same way, the values of address headers in an incoming message can be accessed by reading the relevant property from the AddressingHeaders object, which is accessed from the SoapContext.Addressing property of the incoming message.
Implementing Customized Message Routing Using WSE
In addition to backing WS-Addressing and WS-ReliableMessaging, Microsoft also initially floated a pair of Web service messaging specifications, WS-Routing and WS-Referral, as part of their initial vision of the overall Web services architecture. The likelihood of these two specifications becoming Web service standards seems to be in doubt as no one besides Microsoft has stepped up to support them. Also, much of the functionality described in WS-Routing is now being provided by WS-Addressing, which has the benefit of a much broader base of support.
WSE version 1.0 supported both WS-Routing and WS-Referral, and these functionalities have been carried forward in WSE 2.0. However, since WSE 2.0 also supports WS-Addressing, it is unclear whether this support will continue. As this book goes to press, the WSE team is deciding whether or not to remove support for WS-Routing and most of WS-Referral that was included in WSE 1.0 from the final release of the 2.0 version, so don’t be surprised to find changes in these messaging functionalities in the final version of the product. Since the pre-Beta version that I am working from still supports WS-Routing and WS-Referral, I will discuss these specifications and their WSE implementation because if you are looking to implement a decision-based message routing topology, WSE likely provides the best implementation for message routing without having to write it all from scratch. Remember, however, that this type of routing topology will prove useful only between endpoints that implement WSE.
The WS-Routing Model
Rather than the endpoint-based mechanism of WS-Addressing, WS-Routing defines a routing model based on a message path. In this model, when a Web service creates a new message to be sent using a WS-Routing–based topology, the message has a path element block in the message header that contains all of the information required to route messages. The initial sender and ultimate receiver in a routing topology are represented by the from and to elements, respectively. The following example shows a message created by an endpoint named Tom and sent directly to an endpoint named Fred:
http://example.com/routing/dosomething soap://fred.example.com soap://tom.example.com uuid:84b9f5d0-33fb-4a81-b02b-5b760641c1d6 ⋮
⋮
As you can see in this example, the required action element is analogous to the SOAPAction field in the HTTP header and is used to specify a URI that represents the intended action for the SOAP message. Of course, the from and to elements show that this message was sent from Tom to Fred. The id element specifies a GUID to uniquely reference the message. The to, from, action, and id elements are created by the initial sender of the message and are not modified by any other intermediary endpoints along the message path. The other two elements (which in this example are empty) are fwd and rev, which represent the forward and reverse message paths.
When sending messages along a complex message path, you can specify both the forward path that the message should take to the ultimate receiver and the reverse path that the response message should take back to the initial receiver. In addition, SOAP routers along the message path modify the forward and reverse message paths as needed by the routing topology. The following example assumes that when a Web service router, named Bettie, forwards the message on to Fred, a new via element is created in the reverse path so that the message will return via Bettie:
http://example.com/routing/dosomething soap://fred.example.com soap://bettie.example.com soap://tom.example.com uuid:84b9f5d0-33fb-4a81-b02b-5b760641c1d6 ⋮
⋮
Also, note in this example that Bettie removed the topmost via element in the forward path and added it to the reverse path before resending the message.
In the preceding example, you might have noticed that all of the URI values in the to, from, and via elements are prefixed with soap rather than http. This is because WS-Routing introduces a new URI scheme to use when addressing Web services that are being used as SOAP routers and that are implementing WS-Routing. This scheme prefixes the URI in the routing path block with soap, which is used regardless of the underlying transport. Of course, this addressing mechanism is applicable only to WS-Routing and does not extend to any other reliable messaging specifications.
In WS-Routing, any intermediaries that move a message along the message path are specified using the via element, wherein the order of the via elements in the message’s fwd element indicates the order of the intermediaries. If a message does not have a to element, the last via element denotes the ultimate destination of the message.
The complete forward message path can be created by the initial sender of a WS-Routing message, or it can be constructed and modified by the various SOAP routers as the message moves along the message path. In fact, in many cases the exact message path isn’t known to a sender or routing endpoints dynamically modify the message path based on the contents of the message, and there’s no reason why the service that the sender thinks is the ultimate receiver can’t continue to send the message along paths unknown to the sender.
After a SOAP router validates the routing information for the incoming message, the first change that the router must make to the message header is to remove the topmost via element that contains its own URI. A Web service can add more via elements when there is a need to forward the message to additional Web services. It can also add a via element that represents a recommended return path for the response message as the topmost via element in the reverse path described by the rev element. After this, the service must send the message on to the URI listed in the topmost via element in the forward path or to the URI listed in the to element if no more via elements exist in the forward path.
The WS-Referral Model
WS-Routing describes a mechanism for a Web service acting as a SOAP router to modify via elements in the message routing header for the purpose of providing dynamic message routing capabilities. The question that WS-Routing doesn’t answer, or any other existing specification for that matter, is how individual SOAP routers negotiate these message paths and how routing topologies might be constructed. WS-Referral describes a mechanism by which a Web service examines the message path for endpoints that match a set of referrals the service knows about. When a match is found, the service can change the message path to send the message to the preferred endpoint instead. The set of referrals that a SOAP router needs to know about can be maintained locally, although WS-Referral does specify how to do this. WS-Referral also enables a SOAP router to query for referral statements at other routers as well as to request that a router accept a new referral statement.
WS-Referral clearly builds upon WS-Routing, but the two specifications are designed to be orthogonal, and WS-Referral can be implemented with any scheme that defines a SOAP actor or endpoint. The end result of WS-Referral is that it enables a routing topology to dynamically route messages more easily to support advanced routing behaviors such as failover, load balancing, progressive path discovery, and outsourcing of service hosting. Perhaps the primary benefit of WS-Referral is the ability to separate the external endpoint for a Web service from the actual services that perform the work, thus insulating the client from any knowledge of the back-end architecture of the routing topology and providing a fixed address that’s independent of any activity at the back end.
Referral Statement
At the core of the WS-Referral specification is the notion of a referral statement. Unlike the routing header, which is contained in the SOAP message being routed, the referral statement is maintained locally at the SOAP router, where it’s used to provide the necessary logic for conditional message routing. The following referral statement present on the SOAP router named Tom dictates that if a message is received that’s intended for a SOAP router named Fred, it should first be forwarded to a SOAP router named Bettie on its way to Fred.
soap://fred.example.com soap://bettie.example.com uuid:fa469956-0057-4e77-962a-81c5e292f2ae
When Tom processes an incoming message in which the value of one of the via elements or the to element is equal to soap://fred.example.com, the service adds a via element equal to soap://bettie.example.com to the forward message path in the header and forwards the message to Bettie. When Bettie receives the message, that service will continue to follow the routing path in the header and send the message on to Fred. As long as Tom persists this referral statement, all messages received by Tom on their way to Fred will be forwarded through Bettie.
The way that a SOAP router uses a referral statement is that when a message is received, the Web service checks the referral statements that it knows about for a match between a URI specified in the for element of the referral statement and the to element or any via elements in the forward message path in the routing header. When a match is found, the logic in the if element is checked. If both are satisfied, the message is forwarded to one of the addresses specified in the go statement. In addition, two SOAP endpoints can communicate to negotiate new referrals through a process of referral querying and registration.
Referral Query
Rather than rely on a static message routing topology in which referrals never change or must be updated manually at a machine, WS-Referral specifies a mechanism through which individual SOAP routers can communicate to coordinate referral information and better route messages. The first part of this mechanism is a referral query, which is a two-way messaging exchange by which one router queries for referral statements that are registered at a second SOAP router. If Web service Fred queries Tom for any referrals relating to messages headed for Bettie, they might look like the following request message:
http://schemas.xmlsoap.org/ws/2001/10/referral#query soap://tom.example.com uuid:e5e8d792-abe7-4476-91d0-856fbdf4a958 ⋮
soap://bettie.example.com
In this example, Fred uses the for child of the query element in the message body to ask Tom whether any referral statements for handling messages are currently on the way to Bettie.
In a two-way exchange, each request must have a response. Upon receiving the previous request, Tom checks the referrals in the referral cache for any referrals that match the value of the for element. If any referrals match, they are returned in the query response message. If no matching referrals exist, an empty response is returned. The following is an example referral query response to the previous message:
http://schemas.xmlsoap.org/ws/2001/10/referral#queryResponse soap://Fred.example.com uuid:cf014249-0e2a-4f8b-9002-13a7de916be0 uuid:e5e8d792-abe7-4476-91d0-856fbdf4a958 ⋮
soap://bettie.example.com soap://bettie.example.com uuid:fa469956-0057-4e77-962a-81c5e292f2ae
Notice that the entire referral statement that matches the supplied for value is returned inside a queryResponse block in the message body. If multiple referral statements match the query, each is returned in a separate ref block. Referral queries provide a way to get a referral statement from a SOAP router. The second part of the referral negotiation mechanism involves registering a referral statement at a second SOAP router.
Referral Registration
A Web service router can request that another router either accept or reject a referral statement. Like a referral query, referral registration is a two-way message exchange. A registration request from Fred to Tom might look like this:
http://schemas.xmlsoap.org/ws/2001/10/referral#register soap://tom.example.com uuid:cf014249-0e2a-4f8b-9002-13a7de916be0 ⋮
soap://bettie.example.com soap://bettie.example.com uuid:fa469956-0057-4e77-962a-81c5e292f2ae
In this example, Tom is requested to register the referral to Bettie that we saw earlier. If Tom accepts the referral statement, the statement is appended to the referral cache and a positive registration response is generated as follows:
http://schemas.xmlsoap.org/ws/2001/10/referral#registrationResponse soap://fred.example.com uuid:e5e8d792-abe7-4476-91d0-856fbdf4a958 uuid:fa469956-0057-4e77-962a-81c5e292f2ae ⋮
The presence of the registrationResponse element indicates that the registration succeeded. If Tom fails to accept the referral, a fault is generated as follows:
http://schemas.xmlsoap.org/ws/2001/10/referral#registrationResponse soap://tom.example.com uuid:e5e8d792-abe7-4476-91d0-856fbdf4a958 uuid:fa469956-0057-4e77-962a-81c5e292f2ae ⋮
r:registrationFault Registration Fault 36000000
Using query and registration requests, it’s possible to dynamically maintain a routing topology by adding or removing referrals when necessary. The invalidates element is used to dynamically remove a referral statement from the cache. When an accepted referral contains an invalidates element, its child vid element references another referral that should no longer be used.
Unfortunately, dynamic referral registration is an ideal mechanism for staging man-in-the-middle attacks. If a malicious user can trick a router into accepting a counterfeit referral, that router can then be used to send all subsequent messages to a malicious service. For this reason, before a SOAP router accepts a referral from another SOAP router, a secure level of trust must first be established between the two routers. At a minimum, both parties should be using digital signatures and a trusted mechanism to establish mutual authentication between both services, such as are described by WS-Security and WS- SecureConversation, which I discuss in Chapters 5 and 8, respectively.
As an alternative to the referral query and registration exchange processes, WS-Referral also permits referral statements to be transported in the SOAP message header, which lets you essentially piggyback referrals with regular SOAP messages. When you do this, the collection of referrals is sent within the referrals block in the SOAP header. In this type of exchange, good security practices should still be observed.
WSE Support for WS-Routing and WS-Referral
The support for WS-Routing and WS-Referral in WSE is implemented in the routing and referral pipeline filters. When you send a Web service request using WS-Routing, WSE lets you add one or more Path objects to the SoapContext for the outgoing message. Within the Path object, you can define a collection of Via objects in the Fwd object that defines the endpoints along the forward message path for each desired destination on the message path. When the message is processed by the routing output filter, this information is used to generate the prescribed routing header, which conforms to WS-Routing.
Likewise, the routing input filter processes the routing header information for incoming messages, generates a Path object related to the SoapContext object for the incoming message, and removes the routing header information. This routing information persists in the SoapContext for this message until all other message processing is complete and the message is ready to be forwarded to the next Web service in the message path. When the message is sent, it’s processed by the routing output filter, where the routing header is re-created using the persisted path information. The new request message is then sent on to the next SOAP router in the message path.
WSE manages referral statements locally using a referral cache, which is denoted by the referrals element and contains a referral element for each referral that the local service knows about. Before an incoming message hits the routing input filter, it’s handled by the referral input filter. This filter checks the incoming message for referral headers and referral query or registration requests. If referrals exist, the filter generates a ReferralCollection object to contain the referral header information. The filter also scans the message routing path for any matches with referral statements stored locally in the referral cache. When matches are found, the values of the via elements are modified according the rules in the referral statement.
Introduction to Reliable Messaging
SOAP is a high-level protocol in the overall Web services protocol stack whose main job is to provide a package for XML data, the metadata needed by the recipient, and a binding to a transport. Therefore, SOAP relies on lower-level protocols, such as HTTP, SMTP, and TCP to get the SOAP message and its data payload from endpoint to endpoint. When you send a SOAP message using TCP, HTTP, or any other potential transport protocol for SOAP, these transports see the message path only as a pair of endpoints. Unlike the protocols to which they’re bound, SOAP messages need a more holistic view of where they’re headed. Certain Web service implementations may require a SOAP message be modified by a specific sequence of Web servers before reaching its ultimate destination. A Web service may also need to be able to delegate the processing of an incoming message to other services. There might even be a need during this message routing process for a SOAP message to be transferred from one transport to another, say from HTTP to TCP. Of course, the core SOAP specification doesn’t provide a mechanism for reliably addressing SOAP messages through this kind of complex message routing.
While you generally think of Web services using a two-way, request-response messaging behavior, Web services also support other types of messaging behaviors, including asynchronous messaging. The Web Services Description Language specification (WSDL) identifies the following Web service messaging behaviors:
Request-response in this synchronous messaging scenario, the client sends a request message to the Web Service and receives response message back from the service.
One-way a type of asynchronous messaging, the client sends a message, but a response message is not required or expected. This might be used when a client needs to send period status updates to a Web service.
Solicit-response The Web Service sends a message to the client, and the client then sends back a correlated reply message. This might be used when a Web service needs to poll clients for status information.
Notification another type of asynchronous messaging; the Web Service sends a message to one or more clients without requiring a response. This might be used for clients who have subscribed to event notifications from a Web service.
An equally important concern in SOAP messaging is the issue of message reliability. If we cannot guarantee that messages will be transmitted reliably between endpoints, all of the expected benefits of Web services cannot be realized. Since we do live in a world of unreliable networks, there needs to be a mechanism to enable a message sender to verify that one or a series of SOAP messages have indeed reached the intended recipient. Therefore, reliability for SOAP messaging must address the following issues:
Message ordering The need to order a series of related messages regardless of when they actually arrive.
Message acknowledgment The need to be able to send a response back to the sender stating that one or more messages in a series have been received.
Message delivery policies The need for an endpoint to be able to request specific reliability behaviors for requests, such as the number of times that a failed message should be re-sent and how long to wait before resending.
is transport independent, and WS-ReliableMessaging, which defines mechanisms for message acknowledgment and ordering and for declaring messaging policies.
in real- world applications, your Web service might need to implement a more complex message-routing scenario, such as dynamic, decision-based routing; routing through firewalls or intermediaries; or even using multiple transport protocols. For example, consider a Web service application that has been scaled out to run on multiple Web servers and behind a firewall. In such a scenario, a load- balancing application could be used to route request messages to whichever Web server was best able to handle the load. For example, such an application might determine, based on relative loads, whether to route a request to a processing center located on the East Coast or one located on the West Coast. In a multiple-message transaction, it would then be important that all future requests be sent directly to the application center where the original request was being handled. If this level of specificity for addressing is not supported in the message itself, then the routing application would need to do extra work to determine which processing center should handle all subsequent incoming messages from the client and would likely require the expense of having to maintain a server-side process that includes state information.
In a scenario like this, it is important to have an addressing mechanism that transcends the simple point-to-point functionality provided by the message transports. Among other benefits, such a mechanism would enable any intermediary Web services, such as routers, to have all of the addressing information that they need to ensure that the messages end up in the right place. To provide this consistent addressing for Web services, the WS-Addressing specification defines two constructs for conveying message addressing information: endpoint references and message headers. In this definition, an endpoint is simply an addressable resource to which a SOAP message can be sent; the resource can be a Web service or client application, a SOAP router, or any other SOAP-aware entity.
Addressing Endpoint References
In the reliable messaging model, SOAP messages are sent between and via endpoints. To address a messaging endpoint, we need to be able to explicitly, uniquely, and consistently reference such endpoints. As an additional hurdle, endpoint references must be discoverable so that messages can be addressed dynamically. Of course, as with Web Services Description Language (WSDL), before an endpoint reference can be consumed it must first be described. WS- Addressing introduces a mechanism for describing endpoint references.
From a WSDL standpoint, endpoint description information would most logically be defined within the Service element of a WSDL document; however, WSDL 1.1 does not allow for the extensibility of this element. To get around this limitation, WS-Addressing defines a new typed EndpointReference element that can be used within a Web service description document, such as WSDL, to describe a referenced endpoint. The EndpointReference element supports the following child elements:
Address Provides the Uniform Resource Identifier (URI) of the endpoint, which can be a network or logical address.
ReferenceProperties Provides application-specific information that must be supplied when addressing messages to a particular endpoint.
PortType Specifies the WSDL port type of the referenced endpoint.
ServiceName Specifies the WSDL service that defines the referenced endpoint. This element can also include a PortName attribute that references the name of the WSDL port definition for the endpoint.
Policy Can contain one or more policy assertions, which can be used to describe the policies that are enforced at an endpoint.
The following is an example of an endpoint reference for the CollectionService Web service:
In this example, the Policy element would contain a policy assertion element, which I’ll describe along with a detailed discussion of Web service policies in Chapter 6. By adding this endpoint reference to the WSDL file that describes the CollectionService Web service, you can dynamically assign a policy behavior to the endpoint. In addition to being used in description documentation, the EndpointReference element is also the base type used when describing an endpoint in one of the SOAP message headers defined by WS- Addressing.
Addressing Message Headers
In addition to defining a mechanism for referencing message endpoints, WS- Addressing also specifies a set of message headers that can be used during transport to define the endpoints on the message path. When endpoint information is transported in SOAP headers, this information can be accessed by messaging intermediaries when they need to make intelligent routing decisions. The following SOAP message header elements, which are child elements of the Header element, are defined by WS-Addressing:
To Contains information about the immediate message recipient in the form of a URI.
From Contains information about the message sender in the form of the endpoint reference type.
ReplyTo Contains information about the endpoint to which a reply message should be sent in the form of the endpoint reference type.
Recipient When a message is being routed via one or more intermediaries, can be used to specify an intended ultimate message recipient and is in the form of an endpoint reference type.
FaultTo Contains information about the endpoint to which any fault response messages generated by the request should be sent, in the form of the endpoint reference type.
Action A URI that represents the action for the message, which can be the same as the value of SOAPAction in an HTTP message header.
MessageID A URI that is used to uniquely identify a message.
RelatesTo Used to relate a message to another message by relationship type. This optional element contains a URI that is the MessageID value of the related message. The relationship type is specified using the element’s RelationshipType attribute, where at this point the only accepted value is Response.
The following SOAP request message to the DocumentService shows the use of WS-Addressing message headers.
In this example, the URI value in the From element that defines the endpoint of the requesting party is http://schemas.xmlsoap.org/ws/2003/03/addressing/role/anonymous . This URI is used when a fixed URI cannot be assigned to the Address value of an endpoint. Of course, this anonymous URI value cannot be used with the To element, and if the value is specified in the ReplyTo or FaultTo element, there must be an alternative mechanism for communicating this information.
WSE supports the headers defined in WS-Addressing and, by default, generates a set of addressing headers for all outbound SOAP messages. Also, when incoming messages contain addressing headers, the WSE input filters read these headers and generate the relevant programming objects. As I’ll show later in this chapter, the WSE API can be used to add or change the values of these message headers or to access the addressing information for incoming messages.
Message Reliability for Web Services
While WS-Addressing helps to solve the challenges of consistently describing and accessing Web service endpoints, the general issues of getting messages reliably between message endpoints are being addressed in another specification. A Web service needs to be able to determine when messaging has become unreliable so that it can adjust accordingly, which might involve resending certain messages or delaying the processing of messages until other messages arrive. To that end, IBM, BEA Systems, and Microsoft have again teamed up to draft WS-ReliableMessaging, a specification that attempts to guarantee the reliability of SOAP messaging by defining message acknowledgments, message ordering, and reliability policies
Message Ordering
When a Web services–based interaction comprises a series of SOAP messages, it’s conceivable that because of transmission on unreliable networks these messages might not be received in the order in which they were sent. When messages in a series are lost or delayed, the interaction can become unreliable. This can cause problems that require advanced application logic for handling cases when the message sender assumes that as a result of an earlier message the Web service is in a certain state, but when messages are received out of order, this might not be the case.
To track the order of Web service requests, WS-ReliableMessaging introduces the Sequence SOAP header element. When a client communicates with a Web service implementing WS-ReliableMessaging, the Sequence element is used to number the messages in a sequence, set a time-out period for a sequence, and to declare the end of a sequence. The Sequence element implements the following child elements:
Identifier A unique identifier for the sequence, which leverages the Identifier element defined in the Web Service Security Addendum specification and which is qualified against the http://schemas.xmlsoap.org/ws/2002/07/utility namespace.
MessageNumber A value that defines the sequence number for the message, wherein numbers are assigned as consecutive unsigned integers starting at 1 and increasing by 1 throughout the lifetime of the conversation.
LastMessage Declares that a message is the last in the sequence.
Expires Declares a time in the future when the sequence expires, whether or not the LastMessage element has been used.
The following is an example of a message containing the Sequence header; this message is the third in a sequence, which is defined by the GUID in the Identifier element:
Message Receipt Acknowledgments
Since the Web service architecture enables asynchronous messaging, there is no requirement that a response message be returned for any given request message, as there would be in two-way messaging. However, from the standpoint of message reliability, such an acknowledgment in essence closes the loop on the request process. In asynchronous messaging and without receipt acknowledgments, the requesting party has no way of knowing whether the request message was received. In this case, if there was unreliability on the network, it will likely be manifested later in the interaction as failures that result from having both parties in a different application state. For example, an application might send a one-way request to open a “shopping cart” session, with items to be added in later requests. If this request was not received, subsequent requests to add items to the “cart” will fail since the Web service never received the first request to create the cart session.
One solution might be simply to return a response message that a new cart session was created, essentially making it a two-way messaging scenario. While this dedicated acknowledgment message solves this reliability issue, it is inefficient to generate such a response message, particularly in situations in which both parties are regularly exchanging messages. In such cases, it would be more efficient to piggyback the acknowledgment in the header of an otherwise unrelated message. WS-ReliableMessaging proposes the use of a SequenceAcknowledgment header element that is used to return a receipt acknowledgment for one or more messages in a given sequence, either in an arbitrary response message or in a response created exclusively to return the acknowledgment. A SequenceAcknowledgment element supports the following child elements:
Identifier A unique identifier for the sequence, which leverages the Identifier element defined in the Web Service Security Addendum specification and is qualified against the http://schemas.xmlsoap.org/ws/2002/07/utility namespace.
AcknowledgmentRange Defines a range of one or more sequence numbers that identifies messages that have been received, where the range is defined by the integer values of the Upper and Lower attributes.
An acknowledgment for a single message in a sequence will have the same sequence number for both Upper and Lower attributes. If the range of messages in the sequence being acknowledged is not contiguous, more than one AcknowledgmentRange element can be used. The following example shows the inclusion of a SequenceAcknowledgment element in an acknowledgment response message:
This acknowledgment indicates that message number 3 in the sequence was not received and that the requester should resend this message.
A Web service can wait for a negotiated amount of time before returning an acknowledgment, which gives it time to attach it to another outgoing message. However, once this limit has been reached, the service must generate a response message solely to return the acknowledgment, as in the preceding example. Of course, at any point the AckRequested header element can be sent with any message to the Web service to explicitly request that an acknowledgment for the sequence be returned. This element supports the following child element:
IdentifierA unique identifier for the sequence, which leverages the Identifier element defined in the Web Service Security Addendum specification and which is qualified against the http://schemas.xmlsoap.org/ws/2002/07/utility namespace.
The following message includes a request for a series acknowledgment:
Reliable Messaging Policies
In addition to the functionalities just discussed that use reliability headers, WS- ReliableMessaging also defines a set of policy assertions that can be published by a Web service to declare which reliable messaging functionalities are required when accessing the endpoint. These policy assertions include the following general categories:
Specification version The version of WS-ReliableMessaging supported by the endpoint.
Delivery assurance The number of times a message can be delivered and whether message sequences must be delivered in order.
Sequence expiration The lifetime of a messaging sequence.
Inactivity time-out The time-out period after which a sequence is considered terminated.
Retransmission interval The time that an application should wait for an acknowledgment before resending the message.
Acknowledgment interval The time that a Web service should wait for a response message to be sent back to the requester before generating a stand-alone acknowledgment message.
WSE Support for Reliable Messaging
In version 2.0, WSE supports the WS-Addressing specification, but WS-ReliableMessaging is not yet supported. WSE support for WS-Addressing consists of a set of input and output message filters that read and write the SOAP headers defined in WS-Addressing. WSE automatically adds the appropriate addressing headers to all outgoing SOAP messages. In addition, WSE provides a full API for programmatically creating and accessing addressing objects, and you can explicitly define the addressing objects for outgoing messages to override the values that are automatically generated by WSE.
Creating Custom Address Headers
One of the things that you can do with WSE is to explicitly define the ReplyTo and FaultTo headers in an outbound message. This lets you redirect this type of response message to a different endpoint than the one from which the request was originally sent. When using the WSE, this can be accomplished programmatically by instantiating both a ReplyTo and a FaultTo object in the Microsoft.Web.Services.Addressing namespace, defining appropriate address URI values for each, and adding them to the SoapContext for the outgoing message, as in the following example:
// Create a new SoapContext for the request messageSoapContext myContext = myService.RequestSoapContext;
// Define a different reply address for the requestReplyTo replyAddress = new ReplyTo(new Uri("http://example.com/DocumentService/ResponseHandler.asmx"));
// Define a reply address for fault messagesFaultTo faultAddress = new FaultTo(new Uri("http://example.com/DocumentService/FaultHandler.asmx"));
// Add the custom ReplyTo and FaultTo addressing headers to the SoapContext myContext.Addressing.ReplyTo = replyAddress;myContext.Addressing.FaultTo = faultAddress;
// Security headers added here
// Call the GetDocument methodmyService.GetDocument(docID);
Based on this code, WSE generates the following SOAP request message:
In the same way, the values of address headers in an incoming message can be accessed by reading the relevant property from the AddressingHeaders object, which is accessed from the SoapContext.Addressing property of the incoming message.
Implementing Customized Message Routing Using WSE
In addition to backing WS-Addressing and WS-ReliableMessaging, Microsoft also initially floated a pair of Web service messaging specifications, WS-Routing and WS-Referral, as part of their initial vision of the overall Web services architecture. The likelihood of these two specifications becoming Web service standards seems to be in doubt as no one besides Microsoft has stepped up to support them. Also, much of the functionality described in WS-Routing is now being provided by WS-Addressing, which has the benefit of a much broader base of support.
WSE version 1.0 supported both WS-Routing and WS-Referral, and these functionalities have been carried forward in WSE 2.0. However, since WSE 2.0 also supports WS-Addressing, it is unclear whether this support will continue. As this book goes to press, the WSE team is deciding whether or not to remove support for WS-Routing and most of WS-Referral that was included in WSE 1.0 from the final release of the 2.0 version, so don’t be surprised to find changes in these messaging functionalities in the final version of the product. Since the pre-Beta version that I am working from still supports WS-Routing and WS-Referral, I will discuss these specifications and their WSE implementation because if you are looking to implement a decision-based message routing topology, WSE likely provides the best implementation for message routing without having to write it all from scratch. Remember, however, that this type of routing topology will prove useful only between endpoints that implement WSE.
The WS-Routing Model
Rather than the endpoint-based mechanism of WS-Addressing, WS-Routing defines a routing model based on a message path. In this model, when a Web service creates a new message to be sent using a WS-Routing–based topology, the message has a path element block in the message header that contains all of the information required to route messages. The initial sender and ultimate receiver in a routing topology are represented by the from and to elements, respectively. The following example shows a message created by an endpoint named Tom and sent directly to an endpoint named Fred:
As you can see in this example, the required action element is analogous to the SOAPAction field in the HTTP header and is used to specify a URI that represents the intended action for the SOAP message. Of course, the from and to elements show that this message was sent from Tom to Fred. The id element specifies a GUID to uniquely reference the message. The to, from, action, and id elements are created by the initial sender of the message and are not modified by any other intermediary endpoints along the message path. The other two elements (which in this example are empty) are fwd and rev, which represent the forward and reverse message paths.
When sending messages along a complex message path, you can specify both the forward path that the message should take to the ultimate receiver and the reverse path that the response message should take back to the initial receiver. In addition, SOAP routers along the message path modify the forward and reverse message paths as needed by the routing topology. The following example assumes that when a Web service router, named Bettie, forwards the message on to Fred, a new via element is created in the reverse path so that the message will return via Bettie:
Also, note in this example that Bettie removed the topmost via element in the forward path and added it to the reverse path before resending the message.
In the preceding example, you might have noticed that all of the URI values in the to, from, and via elements are prefixed with soap rather than http. This is because WS-Routing introduces a new URI scheme to use when addressing Web services that are being used as SOAP routers and that are implementing WS-Routing. This scheme prefixes the URI in the routing path block with soap, which is used regardless of the underlying transport. Of course, this addressing mechanism is applicable only to WS-Routing and does not extend to any other reliable messaging specifications.
In WS-Routing, any intermediaries that move a message along the message path are specified using the via element, wherein the order of the via elements in the message’s fwd element indicates the order of the intermediaries. If a message does not have a to element, the last via element denotes the ultimate destination of the message.
The complete forward message path can be created by the initial sender of a WS-Routing message, or it can be constructed and modified by the various SOAP routers as the message moves along the message path. In fact, in many cases the exact message path isn’t known to a sender or routing endpoints dynamically modify the message path based on the contents of the message, and there’s no reason why the service that the sender thinks is the ultimate receiver can’t continue to send the message along paths unknown to the sender.
After a SOAP router validates the routing information for the incoming message, the first change that the router must make to the message header is to remove the topmost via element that contains its own URI. A Web service can add more via elements when there is a need to forward the message to additional Web services. It can also add a via element that represents a recommended return path for the response message as the topmost via element in the reverse path described by the rev element. After this, the service must send the message on to the URI listed in the topmost via element in the forward path or to the URI listed in the to element if no more via elements exist in the forward path.
The WS-Referral Model
WS-Routing describes a mechanism for a Web service acting as a SOAP router to modify via elements in the message routing header for the purpose of providing dynamic message routing capabilities. The question that WS-Routing doesn’t answer, or any other existing specification for that matter, is how individual SOAP routers negotiate these message paths and how routing topologies might be constructed. WS-Referral describes a mechanism by which a Web service examines the message path for endpoints that match a set of referrals the service knows about. When a match is found, the service can change the message path to send the message to the preferred endpoint instead. The set of referrals that a SOAP router needs to know about can be maintained locally, although WS-Referral does specify how to do this. WS-Referral also enables a SOAP router to query for referral statements at other routers as well as to request that a router accept a new referral statement.
WS-Referral clearly builds upon WS-Routing, but the two specifications are designed to be orthogonal, and WS-Referral can be implemented with any scheme that defines a SOAP actor or endpoint. The end result of WS-Referral is that it enables a routing topology to dynamically route messages more easily to support advanced routing behaviors such as failover, load balancing, progressive path discovery, and outsourcing of service hosting. Perhaps the primary benefit of WS-Referral is the ability to separate the external endpoint for a Web service from the actual services that perform the work, thus insulating the client from any knowledge of the back-end architecture of the routing topology and providing a fixed address that’s independent of any activity at the back end.
Referral Statement
At the core of the WS-Referral specification is the notion of a referral statement. Unlike the routing header, which is contained in the SOAP message being routed, the referral statement is maintained locally at the SOAP router, where it’s used to provide the necessary logic for conditional message routing. The following referral statement present on the SOAP router named Tom dictates that if a message is received that’s intended for a SOAP router named Fred, it should first be forwarded to a SOAP router named Bettie on its way to Fred.
When Tom processes an incoming message in which the value of one of the via elements or the to element is equal to soap://fred.example.com, the service adds a via element equal to soap://bettie.example.com to the forward message path in the header and forwards the message to Bettie. When Bettie receives the message, that service will continue to follow the routing path in the header and send the message on to Fred. As long as Tom persists this referral statement, all messages received by Tom on their way to Fred will be forwarded through Bettie.
The way that a SOAP router uses a referral statement is that when a message is received, the Web service checks the referral statements that it knows about for a match between a URI specified in the for element of the referral statement and the to element or any via elements in the forward message path in the routing header. When a match is found, the logic in the if element is checked. If both are satisfied, the message is forwarded to one of the addresses specified in the go statement. In addition, two SOAP endpoints can communicate to negotiate new referrals through a process of referral querying and registration.
Referral Query
Rather than rely on a static message routing topology in which referrals never change or must be updated manually at a machine, WS-Referral specifies a mechanism through which individual SOAP routers can communicate to coordinate referral information and better route messages. The first part of this mechanism is a referral query, which is a two-way messaging exchange by which one router queries for referral statements that are registered at a second SOAP router. If Web service Fred queries Tom for any referrals relating to messages headed for Bettie, they might look like the following request message:
In this example, Fred uses the for child of the query element in the message body to ask Tom whether any referral statements for handling messages are currently on the way to Bettie.
In a two-way exchange, each request must have a response. Upon receiving the previous request, Tom checks the referrals in the referral cache for any referrals that match the value of the for element. If any referrals match, they are returned in the query response message. If no matching referrals exist, an empty response is returned. The following is an example referral query response to the previous message:
Notice that the entire referral statement that matches the supplied for value is returned inside a queryResponse block in the message body. If multiple referral statements match the query, each is returned in a separate ref block. Referral queries provide a way to get a referral statement from a SOAP router. The second part of the referral negotiation mechanism involves registering a referral statement at a second SOAP router.
Referral Registration
A Web service router can request that another router either accept or reject a referral statement. Like a referral query, referral registration is a two-way message exchange. A registration request from Fred to Tom might look like this:
In this example, Tom is requested to register the referral to Bettie that we saw earlier. If Tom accepts the referral statement, the statement is appended to the referral cache and a positive registration response is generated as follows:
The presence of the registrationResponse element indicates that the registration succeeded. If Tom fails to accept the referral, a fault is generated as follows:
Using query and registration requests, it’s possible to dynamically maintain a routing topology by adding or removing referrals when necessary. The invalidates element is used to dynamically remove a referral statement from the cache. When an accepted referral contains an invalidates element, its child vid element references another referral that should no longer be used.
Unfortunately, dynamic referral registration is an ideal mechanism for staging man-in-the-middle attacks. If a malicious user can trick a router into accepting a counterfeit referral, that router can then be used to send all subsequent messages to a malicious service. For this reason, before a SOAP router accepts a referral from another SOAP router, a secure level of trust must first be established between the two routers. At a minimum, both parties should be using digital signatures and a trusted mechanism to establish mutual authentication between both services, such as are described by WS-Security and WS- SecureConversation, which I discuss in Chapters 5 and 8, respectively.
As an alternative to the referral query and registration exchange processes, WS-Referral also permits referral statements to be transported in the SOAP message header, which lets you essentially piggyback referrals with regular SOAP messages. When you do this, the collection of referrals is sent within the referrals block in the SOAP header. In this type of exchange, good security practices should still be observed.
WSE Support for WS-Routing and WS-Referral
The support for WS-Routing and WS-Referral in WSE is implemented in the routing and referral pipeline filters. When you send a Web service request using WS-Routing, WSE lets you add one or more Path objects to the SoapContext for the outgoing message. Within the Path object, you can define a collection of Via objects in the Fwd object that defines the endpoints along the forward message path for each desired destination on the message path. When the message is processed by the routing output filter, this information is used to generate the prescribed routing header, which conforms to WS-Routing.
Likewise, the routing input filter processes the routing header information for incoming messages, generates a Path object related to the SoapContext object for the incoming message, and removes the routing header information. This routing information persists in the SoapContext for this message until all other message processing is complete and the message is ready to be forwarded to the next Web service in the message path. When the message is sent, it’s processed by the routing output filter, where the routing header is re-created using the persisted path information. The new request message is then sent on to the next SOAP router in the message path.
WSE manages referral statements locally using a referral cache, which is denoted by the referrals element and contains a referral element for each referral that the local service knows about. Before an incoming message hits the routing input filter, it’s handled by the referral input filter. This filter checks the incoming message for referral headers and referral query or registration requests. If referrals exist, the filter generates a ReferralCollection object to contain the referral header information. The filter also scans the message routing path for any matches with referral statements stored locally in the referral cache. When matches are found, the values of the via elements are modified according the rules in the referral statement.
Subscribe to:
Comments (Atom)
