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!

    12 comments:

    Anonymous said...

    I found this site using [url=http://google.com]google.com[/url] And i want to thank you for your work. You have done really very good site. Great work, great site! Thank you!

    Sorry for offtopic

    Anonymous said...

    Who knows where to download XRumer 5.0 Palladium?
    Help, please. All recommend this program to effectively advertise on the Internet, this is the best program!

    josh eastburn said...

    Yaron,

    Lots of great WCF info on your site.

    On this page, you have an example of using the hashed PasswordDigest along with a username.

    I am trying to call a Java web service with a WCF client that needs to conform to the following SOAP Headers. Do you have any examples of how to accomplish this??

    <soapenv:Header>
    <wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <wsu:Timestamp wsu:Id="Timestamp-29584084" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <wsu:Created>2008-07-09T20:28:37.412Z</wsu:Created>
    <wsu:Expires>2008-07-09T20:28:47.412Z</wsu:Expires>
    </wsu:Timestamp>
    <wsse:UsernameToken wsu:Id="UsernameToken-482906" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <wsse:Username>UserWebService</wsse:Username>
    <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">S5Of09Zuan02wWpbSEx7EDZsefE=</wsse:Password>
    <wsse:Nonce>Ka+AC6oq5ROJ/pHX1sW+Ng==</wsse:Nonce>
    <wsu:Created>2008-07-09T20:28:37.412Z</wsu:Created>
    </wsse:UsernameToken>
    </wsse:Security>
    </soapenv:Header>

    Yaron Naveh said...

    Hi Josh

    There is no way to manipulate WCF API to omit this exact header as it does not support hashed password.

    What you should do is inject this header into the message using a MessageInspector. The nonce should be a random base64 string of that size and the timestamps taken dynamically from the clock.

    Marat Abdulganejev said...

    >>There is no way to manipulate WCF API to omit this exact header as it does not support hashed password.

    Hi, Yaron. Have i understood u correctly and there is no way to authorize using digest in WCF?

    Thing is i have a java WS with wsse authentication behind ssl and i need to write a client to communicate with it.

    I've doing all the
    b.Security.Transport.ClientCredentialType = HttpClientCredentialType.Digest;
    sc.ClientCredentials.HttpDigest.ClientCredential = new NetworkCredential("a", "a").GetCredential(new Uri("host"), "Digest");







    But password keeps being transfered as plain text : (. Is there a way to configure proxy for hashed password to be transferred?

    Yaron Naveh said...

    You can do digest at the transport level. The problem is in the message level, which you do not need.

    Use fiddler to see if the server really adks for digest from the client or only basic.

    Marat Abdulganejev said...

    Thx for quick reply, Yaron!

    I've tried swithing to Transport, but in this case according to Charles no wsse headers passed + server returnes

    System.ServiceModel.FaultException: com.sun.xml.wss.XWSSecurityException: Message does not conform to configured policy [ AuthenticationTokenPolicy(S) ]: No Security Header found; nested exception is com.sun.xml.wss.XWSSecurityException: com.sun.xml.wss.XWSSecurityException: Message does not conform to configured policy [ AuthenticationTokenPolicy(S) ]: No Security Header found

    I've checked with fiddler, and do not see any information regarding authentification mode. But when i use TransportWithMessageCredential wsse headers are passed correctly(though pass is a plain text) and i get

    System.ServiceModel.Security.MessageSecurityException: An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. ---> System.ServiceModel.FaultException: com.sun.xml.wss.XWSSecurityException: Receiver Requirement for Digested Password has not been met; nested exception is com.sun.xml.wss.XWSSecurityException: com.sun.xml.wss.XWSSecurityException: Receiver Requirement for Digested Password has not been met

    Yaron Naveh said...

    Please publish how your request messagae should look like.

    Marat Abdulganejev said...

    It should look like this(at least such request is coming from java clients and works from soapUI, basic auth is not working)
    http://pastebin.com/2Pp55Kf3

    app config
    http://pastebin.com/bAA7tgnC

    Code itself
    http://pastebin.com/WWYhPzUh

    Charles shows that request came like this
    http://pastebin.com/5uUYndrw

    Server responded with
    http://pastebin.com/NgCXB0KM

    Yaron Naveh said...

    WCF does not support digested password in the messgae level.

    You might be able to add this with a custom header instead.

    I would suggest you set up a working Java client and then see how the soap it sends looks like.

    Ben said...

    @Josh and @Marat, did you ever find a solution to your problem.? I have the same issue.

    It is very frustrating to think that we have to revert to using obsolete WSE libraries so that we can get this to work.

    All well and good that Microsoft thought that this was not worth supporting, but nobody told the swathes of corporations who still use this authentication scheme, and force us to deal with it.

    If you found a solution that works with SCF, then I'd love to hear more. You'll find me on Twitter as @junto. http://twitter.com/junto

    I would just be nice to know that this is possible, and that I'm not wasting my time trying to work towards something that is neigh on impossible to achieve!

    morateg said...

    Yep, Ben, finally, i've found the WCF solution, though a pretty ugly one :).

    I'm creating Nonce, Created and hashed password digest myself and adding them into soap request headers by defining own inspector(header added in IClientMessageInspector.BeforeSendRequest) and behaviour(implements IEndpointBehavior) classes where i override soap headers.

    I've spent about two weeks trying to find more elegant solution, but found none :)