Friday, January 30, 2009

Interoperability Gotcha: Order of XML Elements

@YaronNaveh

This time I will talk about the importance of order within xml elements.

Let's say we have a web service with this schema:


<s:element name="root">
 <s:complexType>
  <s:sequence>
   <s:element name="elem1" type="s:string" />
   <s:element name="elem2" type="s:string" />
  </s:sequence>
 </s:complexType>
</s:element">


So of course this is a valid request:


<root>
 <elem1>I'm elem1</elem1>
 <elem2>I'm elem2</elem2>
</root>


But about about this?


<root>
 <elem2>I'm elem2</elem2>
 <elem1>I'm elem1</elem1>
</root>


This is not a valid xml instance!
The reason is that the schema contains the element which requires its sub elements to have order.

So why is it an interoperability gotcha?
The reason is that different soap stacks by different vendors behave differently in such cases. Sometimes even different stacks of the same vendor are divided...

Let's see how a .Net 2.0 proxy would look like:


...
private string elem1;
private string elem2;
...


This proxy can parse both xml instances from above.

This is how the equivalent WCF proxy would look like:


[XmlElement(Order=0)]
public string Elem1
{
...
}

[XmlElement(Order=1)]
public string Elem2
{
...
}


This proxy is stricter and actually enforces the order of elements. So it would only accept the first (legal) instance and will give unexpected results with the second. It would usually not throw an exception but rather ignore the message and use default values for the unordered elements so your fields will have a lot of NULL's and zeros.

How to fix it?

Option 1 (recommended) - Send XML with the correct order.

Option 2 - Change the WSDL to use <all> instead of <sequence>. The former does not require correct order of elements (there are some other subtle differences). Note that in case the WSDL is dynamically generated this may be hard to maintain.

Option 3 - Remove the "Order=..." property from the WCF proxy. Note that when the proxy will be regenerated the "Order" property will come back so this is also a maintenance challenge.

@YaronNaveh

What's next? get this blog rss updates or register for mail updates!

Friday, January 23, 2009

Stateful Security Context Tokens in WCF

@YaronNaveh

When a custom binding is used in WCF it is possible to configure the value of requireSecurityContextCancellation. The name of this property doesn't exactly indicate in what scenarios it should be used.

When the client and server use WS-SecureConversation (by setting authenticationMode to "SecureConversation") the client get some token from the server in the initial handshake and needs to send it in every connection attempt. The server can extract this token and find the relevant data of this session in its memory. But what happens when the server/IIS is restarted? The memory is cleared and the session becomes invalid. The way to overcome this is to set requireSecurityContextCancellation to false (the default is true). In this mode the session key is saved at the client side and it sends it to the server in every request. This means that even if the server is restarted the session key is available in the request. This also means that the size of each request is a little bigger.

Since with this mode the server does not need to save the session key it does not need to be informed when the session is over and from here the property name requireSecurityContextCancellation.

BTW From performance point of view it is not always recommended to use a secure conversation.

Read more on the subject in the MSDN page.

@YaronNaveh

What's next? get this blog rss updates or register for mail updates!

Saturday, January 17, 2009

WCF Sessions Explained

@YaronNaveh

Richard Blewett has published a great post about WCF sessions. He explains how sessions are implemented in each of the bindings and how they can be used.

One addition over this post is that sessions can also be used in WCF via the asp.net compatibility mode. Here, the way to access the session is:


HttpContext.Current.Session["key"] = "value";


In this mode the session is implemented via an HTTP cookie - exactly as with classic .Net 2.0 web services / asp.net. This means that this mode is available only when hosting in IIS and using HTTP transport. This mode should be used in case backward compatibility with an old service/client is required. It should not be used with new services as it adds some overhead to the pipeline.

@YaronNaveh

What's next? get this blog rss updates or register for mail updates!

Monday, January 5, 2009

Signing custom headers in WCF

@YaronNaveh

Recently I had an interesting case with WCF "raw message" contract:


public interface IUniversalContract
{
  [OperationContract(Action = "*", ReplyAction = "*")]
  Message ProcessMessage(Message message);
}


The message I used on the client side contained some SOAP headers. Since this is an untyped message these headers were neither encrypted nor signed. That was a show stopper for me.

I saw this blog post by Nicholas Allen and immediatelly tried the following syntax:


ChannelProtectionRequirements requirements = bindingParameters.Find();
XmlQualifiedName qName = new XmlQualifiedName(header, ns);
MessagePartSpecification part = new MessagePartSpecification(qName);
requirements.OutgoingSignatureParts.AddParts(part, action);


However the request message was still not being encrypted nor signed. I struggled with this issue for some time until I was lucky enough to find Pedro Felix's message in the WCF MSDN forum:


The MSDN docs state that

a) IncomingSignatureParts - Gets a collection of message parts that are signed for messages from client to server

b) OutgoingSignatureParts - Gets a collection of message parts that are signed for messages from server to client


In other words the OutgoingSignatureParts always relates to server outgoing messages while client outgoing stuff is always considered as "incoming". After fixing this the code worked like a charm. I probably should have proactively RTFM myself but at least I felt good for not being the only one who fell for this...

@YaronNaveh

What's next? get this blog rss updates or register for mail updates!

Saturday, January 3, 2009

How to use clear username/password with WCF?

@YaronNaveh

WCF does not allow to authenticate clients with cleartext username/password. While this is generally considered an unsecured mechanism there are some valid reasons to use it (such as SSL passthrough of load balancers like F5's BIG-IP, or for interoperability reasons). So let's see what can still be done with WCF.

First attempt - using basicHttpBinding with MessageClientCredentialType of "Username".
Unfortunetelly this would yield the following exception:


BasicHttp binding requires that BasicHttpBinding.Security.Message.ClientCredentialType be equivalent to the BasicHttpMessageCredentialType.Certificate credential type for secure messages. Select Transport or TransportWithMessageCredential security for UserName credentials.


Second attempt - using basicHttpBinding with TransportWithMessageCredential mode.

Since this mode implies that we need to secure the transport we get any of these exceptions, depending if we are on the client or the server side:


The provided URI scheme 'http' is invalid; expected 'https'.
Parameter name: via



Could not find a base address that matches scheme https for the endpoint with binding BasicHttpBinding. Registered base address schemes are [http].


Third attempt - using wsHttpBinding with MessageClientCredentialType of "Username".

Depending on several other settings, and wheather we're on the client or the server, we would get any of these exceptions:


Object reference not set to an instance of an object.



The service certificate is not provided for target 'http://localhost/MyWebServices/Services/SimpleService.asmx'. Specify a service certificate in ClientCredentials.



The service certificate is not provided. Specify a service certificate in ServiceCredentials.


Forth attempt - using wsHttpBinding with TransportWithMessageCredential mode.

Similarily to the second attempt we get:


The provided URI scheme 'http' is invalid; expected 'https'.
Parameter name: via


OR


Could not find a base address that matches scheme https for the endpoint with binding WSHttpBinding. Registered base address schemes are [http].


Fifth attempt - using customBinding with httpTransport and security element with authenticationMode of UserNameOverTransport

This time we get:


"The 'CustomBinding'.'http://tempuri.org/' binding for the 'SimpleServiceSoap'.'http://tempuri.org/' contract is configured with an authentication mode that requires transport level integrity and confidentiality. However the transport cannot provide integrity and confidentiality."


So it really seems like Microsoft is trying to (im)politely convince us not to use clear username/password. But what can we do for cases where this behaviour is really required?

The solution

The solution s to use ClearUsernameBinding. This binding seamlessly integrates with WCF and allows us to use clear username/password.

Read more about ClearUsernameBinding for WCF.

@YaronNaveh

What's next? get this blog rss updates or register for mail updates!