Saturday, November 22, 2008

Securing Web Services With Username/Password

@YaronNaveh

Many web services authenticate clients using username/password. In the soap message it looks like this:


<wsse:Username>yaron</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">1234</wsse:Password>


You can notice that the username/password are clear (not encrypted). This means that everyone that can see this message can steal the password.

There are typically 3 ways to overcome this.

1. Sending the hashed password only:


<wsse:Username>yaron</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">iuacnq2G/t0nWcYgCMx66UtFlxA=</wsse:Password>


Where the digest is calculated from the password, a timestamp and a nonce.
However this is not really secure. A hacker can use a dictionary attack to extract the password.

2. Protecting the username with SSL.
This is a valid option but is limited to HTTP web service only and denies us from some of the rich WS-Security options. There are some other transports which are inherently secured (like SSL passthrough of load balancers as F5's BIG-IP) but I will not discuss them here.

3. Protecting the username with message-level X.509 certificate.
This is another valid option but sometimes more complex to implement.

In practice if you want to use username/password you would need to decide between options 2&3. Some frameworks, like Microsoft's WCF, even prevents you from using option #1 at all. Nevertheless there are a few exceptions where option #1 is valid:

  • Your network is inherently secured so you are not afraid of password stealers.

  • You are required to interoperate with a service that requires to send clear username/password


  • If you are using WCF and have the above needs you should use the WCF ClearUsernameBinding.

    @YaronNaveh

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

    Thursday, November 13, 2008

    Cryptic WCF error messages (part 3 of N)

    @YaronNaveh

    When you use X.509 certificates you might get the following exception on the client:


    Identity check failed for outgoing message. The expected DNS identity of the remote endpoint was 'localhost' but the remote endpoint provided DNS claim 'WSE2QuickStartServer'. If this is a legitimate remote endpoint, you can fix the problem by explicitly specifying DNS identity 'WSE2QuickStartServer' as the Identity property of EndpointAddress when creating channel proxy.




    The error happens while client tries to authenticate the server. It does so by comparing the server identity as appears in its X.509 to the server identity you configured to expect. If you haven't configured it than it assumes the service url domain name is the expected identity (for example "localhost" is expected for "http://localhost/MyService.svc").

    The solution is to do exactly as the error tells us: Define the expected identity in the configuration. In app.config it can look something like this:


    <client>
    <endpoint address="http://localhost:13037/WCFService54/Service.svc"
    contract="ServiceReference1.IService"
    name="WSHttpBinding_IService">
    <identity>
    <dns value="localhost" />
    </identity>

    </endpoint>
    </client>


    and in the configuration editor as bellow:

    @YaronNaveh

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

    Inconsistent "soap:mustUnderstand" in .Net 2.0

    @YaronNaveh

    It seems that .Net 2.0 web services do not handle soap:mustUnderstand attribute in a consistent manner. This attribute can appear over soap headers in order to require the processing party to fail itself if it cannot process this specific header. Example:


    <soap:Envelope>
     <soap:Header>
      <SomeHeader soap:mustUnderstand="1">1234</SomeHeader>
     </soap:Header>
     <soap:Body>
      ...
     </soap:Body>
    </soap:Envelope>


    Now if our server does not understand SomeHeader an exception should be thrown.

    To begin with, when a SoapHeader is defined in the service/proxy, .Net will not allow us to mark it as "required" since it is considered obsolete:



    This is not really related to mustUnderstand: When the proxy contains required=true it means (or should have at least) that the proxy will fail itself if this value is not supplied by the user. This is different than omitting a mustUnderstand header which will cause the server to fail.

    But the real inconsistency comes with mustUnderstand: When a .Net 2.0 web service gets soap:mustUnderstand header it will always ignore it even if it was not understood. On the other hand, when a .Net 2.0 web service client gets such an header in a response from the server it will throw the following exception:


    SOAP header SomeHeader was not understood.

    Be aware of this inconsistency when you build or consume .Net 2.0 web services.

    @YaronNaveh

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

    Monday, November 10, 2008

    Web Services XML: Nullable AND Optional xsd:string

    @YaronNaveh

    In xml an element can be both optional and nillable:


    <s:element name="Name" type="s:string" minOccurs="0" nillable="true" />


    So in xml this element can have a value:


    <wrapper>
       <s>123</s>
    </wrapper>


    Be NULL:


    <wrapper>
       <s xsi:nil="true" />
    </wrapper>


    Or be omitted at all:


    <wrapper />


    This is a challenge to many web services frameworks since when generating proxy code an element is usually mapped to one language variable. A variable in a programming language does not natively support metadata such as being "nullable" or "optional".

    In some frameworks the solution is to add a "controller" field. For example some Java frameworks would create something like this:


    setName(String Name) {}
    setNameNillable(bool isNillable) {}
    setNameOptional(bool isOptional) {}


    In .Net 2.0 part of this informaiton is in a controller field and part in .Net attributes. For example a nillable string is:


    [System.Xml.Serialization.XmlElement(IsNullable=true)]
    public string s;


    And an optional int is:


    public int i;
    [System.Xml.Serialization.XmlIgnore()]
    public bool iSpecified;


    What happens in .Net when an element is both nullable and optional?
    There is no problem with an int - we can use System.Nullable:


    public int? i;
    [System.Xml.Serialization.XmlIgnore()]
    public bool iSpecified;


    So now "i" can both have a value of null and seperately be specified as ommited.

    String is more challenging since it can have null from the first place so "string?" is meaningless. We would expect to have:


    [System.Xml.Serialization.XmlElement(IsNullable=true)]
    public string s;
    [System.Xml.Serialization.XmlIgnore()]
    public bool sSpecified;


    However for some reason .Net does not generate this for the client side so it does not support out of the box a string which if both optional and nullable.
    The solution is to manually add the sSpecified element and the XmlIgnore attribute as above to the client proxy or the server side code.
    Note: For the client proxy updating the service reference will override any such change so use this method only if you have no other choice. For example use it when you try to interoperate with a framework that requires this.

    @YaronNaveh

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

    .Net 2.0 Web Services: Raw Text Response

    @YaronNaveh

    Let's say you want your .Net 2.0 web service to return a simple string without any wrapping element (except the necessary minimum). Like this for example:


    <soap:Body>
      <HelloWorldResponse xmlns="http://tempuri.org/">
       Hello World
      </HelloWorldResponse>
    </soap:Body>


    If you will use the default service template:


    [WebMethod]
    public string HelloWorld() {
    return "Hello World";
    }


    You'll get a string wrapped with an element "HelloWorldResult":


    <soap:Body>
      <HelloWorldResponse xmlns="http://tempuri.org/">
       <HelloWorldResult>
        Hello World
       </HelloWorldResult>
      </HelloWorldResponse>
    </soap:Body>



    So how to return a raw unwrapped text?
    Just add the "return" attribute in this way:



    [WebMethod]
    [return: System.Xml.Serialization.XmlText()]
    public string HelloWorld() {
    return "Hello World";
    }

    @YaronNaveh

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