Adding WSSecurity Support to IS Web Services

Finally got around to writing a blog entry on how to add support for WS-Security to webMethods Integration Server.

Mark

Mark-

After reading some of the WS Security specification as well as a blog entry of yours, I understand that in order to implement authorization in my custom soap processor I’ll need to extract the wsse:Security element from the soap header and authenticate the token. My web services will be invoked over SSL on a secure network so I believe the following header element can be used:


<wsse:Security soap:mustUnderstand=“1”>
<wsse:UserNameToken xmlns:wsu=“…”>
wsse:UserNameuser</wsse:UserName>
wsse:Passwordpassword</wsse:Password>
</wsse:UserNameToken>
</wsse:Security>

How do I modify my WSDL to ensure that web service consumers include the security header?

My WSDL currently imports an XML Schema that defines the types used by my message elements. I’m not clear on how to define/require header information.

Thanks,
Fred

PS - I realize you address the use of ServiceNet as a means of implementing WS Security in this posting ([URL=“wmusers.com”]wmusers.com), however, my solution does not need to be that robust. I will not be using digital certificates/signatures, etc. Simply an un-encrypted user ID and password.

Fred,

WSDL describes soap message headers using the “message/part” element.

Let’s say you had a CreateOrder service that defined a message called CreateOrderRequestMessage (and also a CreateOrderResponseMessage). The CreateOrderRequestMessage might be defined with a single part initially like this:

[highlight=XML]


[/highlight]
To add a part representing a Security element in the soap header you could do the following assuming you had imported a definition for the wsse:Security element:

[highlight=XML]



[/highlight]
I have been using the WSDL Editor in XML Spy Enterprise Edition to create WSDL’s that use modular design with imported schemas. In a recent blog entry, Mark Griffin turned me on to the new WSDL Editor that is part of the Eclipse 3.1 Web Tools Platform 1.0.

I was able to add a message part that referenced an imported schema in the Eclipse WSDL Editor as well. The only thing I found to be problematic is that the Eclipse-based WSDL Editor did not seem to validate the WSDL adequately, but perhaps I just haven’t found that function yet.

Note: The webMethods Web Services Connector wizard does not understand how to generate code to populate soap headers. It will generate code to populate each message part, but the generated Flow will attempt to add the header to the soap body instead of the soap header. This is not correct since a soap message can only have one body element.

Fortunately, you can just replace the generated call to pub.soap.utils:addBody with a call to pub.soap.utils:addHeaderEntry with virtually the same inputs.

Regular readers will know that I don’t recommend using the web services connector. This is just another reason to avoid it.

HTH,

Mark

Fred,

You might consider enhancing your UsernameToken is to add a Type attribute with a static value of “wsse:PasswordDigest#” to your Password element, add Nonce and Created elements and then generate the password digest value using the algorithm described in the UsernameToken Profile.

The combination of those changes will help protect against replay attacks by allowing you to detect duplicate or expired tokens. You can enhance this further by signing or signing and ecrypting the token

Without the password digest you are not getting any added security from WS-Security although you are adopting a standard that can be expanded in the future.

Good:

[highlight=XML]
<wsse:Security soap:mustUnderstand=“1”>
<wsse:UserNameToken xmlns:wsu=“…”>
wsse:UserNameuser</wsse:UserName>
wsse:Passwordpassword</wsse:Password>
</wsse:UserNameToken>
</wsse:Security>

[/highlight]
Better:

[highlight=XML]
<wsse:Security
xmlns:wsse=“http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd
SOAP-ENV:mustUnderstand=“1”>
wsse:UsernameToken
wsse:Usernameusername</wsse:Username>
<wsse:Password Type=“wsse:PasswordDigest#”>o2zUG6x2/+52pbgEukK08R3isTM=</wsse:Password>
wsse:Noncemyserver:abc123</wsse:Nonce>
wsse:Created2006-02-02T01:18:25.730+00:00</wsse:Created>
</wsse:UsernameToken>
</wsse:Security>

[/highlight]

For those WSDL rookies reading this thread (such as myself), I’ll complete Mark’s example by also indicating that the RequestParameter_SecurityHeader and RequestParameter_Body parts must be mapped to the SOAP Header and Body in the operation definition as follows:


<wsdl:operation name=“createOrder”>
soap:operation/
wsdl:input
<soap:header message=“ns:CreateOrderMessageRequest” part=“RequestParameter_SecurityHeader” use=“literal” />
<soap:body use=“literal” parts=“RequestParameter_Body”/>
</wsdl:input>
wsdl:output
<soap:body use=“literal”/>
</wsdl:output>
</wsdl:operation>

(Disclaimer: I’m sure there are other ways to achieve the example above!)

Now to my question…

When I use XML Spy Enterprise Edition to generate a SOAP Request for my WSDL it creates the wsse:Security SOAP Header Node but obviously has nothing in it. How can I define my WSDL to require that clients provide the UsernameToken as part of the request? My custom soap processor will expect the UsernameToken to exist in order to validate the request, but if my WSDL doesn’t explicitly define that it’s requried how can I expect clients to provide it?

Thanks,
Fred

Fred,

I defined a Security element that specified the structure that our project needs. I used the wsse prefix and WS-Security namespace because our structure conforms to the very openly defined WS-Security UsernameToken spec.

When I use Spy to create a soap request, a username token with the structure I want is included in the header. Here’s the complex type definition we use:

If you want any soap processor who receives your message to be required to understand and process the Security element then you would add an attribute of soap:MustUnderstand=“1” (where “soap” is the prefix of your soap envelope) to the Security element.

According to the soap 1.1 spec, soap processors that do not know now to process a header element (Security in our case) MUST reject the message with a soap:fault/faultcode of “MUSTUNDERSTAND”. You can do this in IS with the built-in service pub.soap.utils:exitUnableToUnderstand.

HTH,

Mark

So, you’re not actually importing the wsse:Security schema into your schema? Instead, you’re reusing the “wsse” prefix and creating your own complex type that contains types identical to those found in wssecurity-secext-1.0.xsd? If this is the case did you assign an arbitrary namespace to wsse?

Also, forgive the stupid question but I tried to import and use the SOAP-ENV prefix to reference the mustUnderstand attribute in my schema. I set the “fixed” attribute of mustUnderstand to “1” with the following statement


<xsd:attribute ref=“SOAP-ENV:mustUnderstand” fixed=“1”/>

but the XML Spy generated SOAP request does not assign the mustUnderstand attribute a value. When I set the mustUnderstand “default” attribute to “1” the generated SOAP request has a value of 1, however, I presume the client can override this value. I suppose it doesn’t really matter as I can code my custom soap processor to reject any request that doesn’t have mustUnderstand=1, however, this approach may not follow the standards that you’ve described in this thread.

Thanks,
Fred

Correct. If I were more of an XML schema whiz, I’m sure that I could restrict the WS-Security schema by extension to accomplish the same thing. This was easier for me. I used the WS-Security namespace to indicate that token is a valid instance of wsse:UsernameToken (almost anything inside a Security tag would be :wink: )

I also noticed the behavior you describe with the soap request created by XML Spy and the value of the MustUnderstand attribute. I think this is just a function of how Spy creates XML documents from schemas and is not a concern.

As for rejecting messages that don’t have the MustUnderstand attribute set, that’s probably not necessary as long as they have a valid UsernameToken. You would return soap faults for other types of WS-Security errors.

The Microsoft WS-Security 1.0 overview document describes possible WS-Security errors this way:

Mark-

Once you extract the user credentials from the SOAP header how are you validating them? Did you build a custom solution or were you able to use any of the existing IS authentication mechanisms?

Ideally, I’d like to create an ACL that my web service users belong to. The target flow services invoked by my custom soap processor would also provide the ACL in the Execute ACL parameter.

The “Controlling Access to SOAP Processors” section of the SOAP Developer’s Guide only offers a solution for RPC and Default SOAP processors, not custom soap processors.

Thanks,
Fred

Fred,

Usually, when an organization goes to trouble of implementing web services security, they have already implemented some type of identity management infrastructure such as LDAP-based authentication or an ID Management vendor such as Netegrity, Oblix or RSA Cleartrust.

In my projects, basic auth was used to limit the clients who could submit requests to the custom soap processor. Once the custom authentication took place (Netegrity/SAML in one case, database-based password hash comparison in another, LDAP in my current project) the client was free to execute the service.

Now, if you need service consumers to assume a different IS user than the user that was authenticated and authorized to submit the request, IS uses a “pluggable” authentication system, meaning that it is possible with a moderate level of effort and perhaps some assistance from WM to replace the default authentication system with another one that relies on LDAP or something similar.

Search on Advantage for info on pluggable authentication and LDAP-based authentication and you should find a whitepaper or two.

Role-based security for webMethods products will be introduced over the next release or two of all products to make it much easier to create custom roles which have access to some administrative functions (viewing error logs for example) but not others (adding users, ports, etc.).

HTH,

Mark

Thanks for the info Mark. The software I’m developing is hosted within a classified and secure government network and at this point I just need to implement some basic authentication as a foundation for extending it later. I assumed that the Execute ACL for my custom soap processor would be set to “Anonymous” and that I’d use the information in the SOAP-Header to somehow authenticate the credentials against existing IS Users and ACLs before invoking the target flow service.


…If you need service consumers to assume a different IS user than the user that was authenticated and authorized to submit the request…

If my custom soap processor has an anonymous ACL then no user is authenticated and authorized before submitting a request, correct? If I provide an ACL for the processor, how are the credentials authenticated against the IS?

Basically, I’m just confused as to how to implement a custom soap processor and provide some level of basic security. I’ll research the pluggable LDAP-based authentication but this seems more robust than what I need.

Thanks,
Fred

After some additional research, I suppose I can just use the com.wm.app.b2b.server.UGClass.checkPassword method to authenticate the credentials in my SOAP Header against a wM User. In this scenario I probably don’t need to implement ACLs at all.

However, I’m still confused about how the Default and RPC SOAP processors authenticate requests. Based on my understanding of these processors I assume that credential information must be provided by the client in the HTTP request in order for the IS to perform Basic HTTP Authentication.

Fred

Fred,

You should be able to set the ACL on your custom soap processor to restrict access to posting soap messages to that processor.

Mark

Allright, I’m missing something conceptual here. If I set an ACL on my custom soap processor and use XML Soap, for example, to submit a SOAP Request I am prompted for credentials. If I provide the appropriate credentials the SOAP Request is processed.

How can client’s making requests of my SOAP processor automatically provide these credentials? I assume it needs to be part of the HTTP request, but I thought the idea of WS-Security is to embed the credentials as part of the SOAP Header. If I’m going to expect my clients to provide credentials in the HTTP request (which is a suitable alternative for me, I’ll just drop the WS-Security SOAP Header) how can I enforce that they do? It’s not part of the WSDL so how do I define this contract?

Fred

Fred,

Sorry. I didn’t mean to muddy the waters. I view controlling ability to post a soap message to IS as a separate event from authenticating the end user represented by the soap request itself.

The username token in the soap message header (or SAML token or X509 Cert token) should represent the end user who is consuming the service. This may be a Portal user or a user logged into some web services-enabled application.

Those applications need to be able to submit the message to IS, but not the general public. I usually assign system-level accounts to applications or portals who need to consume IS-hosted web services, but use WS-Security to authenticate and authorize the end user represented in the request.

Clear as mud?

Mark

OK… since my authentication really only needs to happen at the application level and not at the user level I’ll need to figure out how to submit SOAP requests that contain credentials in the HTTP header.

Thanks!
Fred

Fred,

Which Soap client will be consuming your IS web services? Each client has its own method of setting authentication properties.

Mark

For my testing purposes I’m using a webMethods Connector and XML Spy as my SOAP clients. The actual client will be a COM object developed using the .NET framework and invoked from an ASP. A sub-contractor will be developing the COM object, however, we’re in the process of prototyping that approach ourselves.

I assume that .NET will provide some mechanism for including credentials as part of the HTTP request. Thoughts? Also, will this approach create a “session” between the COM client and wM? I think I should re-authenticate every request (even though all requests will come from the same application) which may be an argument for providing credentials/authentication at the SOAP-Header level.

Fred

Yes, most soap clients provide a method to set the credentials on a soap request. Your .Net developer should know how to do this using C#, VB .Net or the Visual Studio .Net language of choice for your project.

There will be an HTTP session. Some clients can maintain sessions so that multiple requests can be sent without reauthenticating. This is more important when you are using SSL sesssions (HTTPS) due to the amount of time it takes to setup the HTTPS session.

Mark

Careful with the versions of .Net that you use for clients. While there are API calls to force the HTTP credentials be sent on the first request, the actual property is ignored. See [URL=“http://mark.michaelis.net/Blog/CallingWebServicesUsingBasicAuthentication.aspx”]http://mark.michaelis.net/Blog/CallingWebServicesUsingBasicAuthentication.aspx[/URL]
for a description of what needs to be done on the .Net client side.

Ed