A subject that I still see a lot of misunderstanding around is how best to use the UsernameToken when using a user id and password as the basis of authentication for a Web service.
- First and foremost ensure you are protecting password information in the database – preferably by hashing and salting the password
- Use either WSE 3.0 UsernameForCertificate or UsernameOverTransport assertion
- Do not sign or encrypt messages using passwords as they are typically low entropy and can leave you susceptible to offline guessing attacks if an attacker can intercept your message.
- More information:
- Direct Authentication architecture pattern - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/wss_ch3_impdirectauth_wse30.asp
- Implementing Direct Authentication using WSE 3.0 and either SQL, ADAM (or any LDAP directory service), or AD - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/wss_ch3_impdirectauth_wse30.asp
The UsernameToken is based on a Web service security specification (http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf) which describes how a client can provide a Username which should uniquely identify a user and optionally a password or password equivalent that can support autheitcation. This specification includes a range of options some of which are in my opinion useful – and others that should be used with caution.
WSE 2.0 and 3.0 Implementation of UsernameToken
In the Microsoft Web Services Enhancements (WSE) implementation of UsernameToken, a password is used to establish proof–of-possession. The password sent in a UsernameToken message can be a cleartext password or a password equivalent, which is a unique representation of a password, typically created by hashing (and salting) the cleartext password,
Alternatively an XML signature created using the shared secret could be used as the basis of authentication – in which case the service would have to recreate the equivalent XML signature in order to ensure the same shared secret is possessed by both client and service. When a message is signed, the password used to sign the message is never included in the UsernameToken, because including it would compromise the signature.
After the message is received by the service, the AuthenticateToken method of the UsernameTokenManager class is used by WSE to validate the information in the UsernameToken. The AuthenticateToken method returns the password that was used for authentication. This return value is used by WSE to validate the original message. The specific type of validation is based on the password option that was used.
Note: AuthenticateToken can be overridden, allowing UsernameToken to be used in conjunction with custom identity providers. WSE 2.0 and 3.0 supports authenticating tokens based on a Windows user account with the password sent as plaintext (see options later in this document). In all other cases, a custom UsernameTokenManager is required.
WSE defines three password options used to send a password in the message: SendNone, SendPlainText, and SendHashed.
This option may be used in two scenarios. The first is where the sender signs the message with an XML signature based on their password. The password is never sent in the SOAP message, The recipient then needs to use the same password in order to verify the sender’s signature. This is accomplished by returning the password used to sign the message from the AuthenticateToken in the UsernameToken manager. While I believe it would be possible to implement this in WSE 3.0 using API’s I believe WSE 3.0 has simpler and more secure approaches. (Keep reading)
The second scenario is where a UsernameToken is being used to flow identity across tiers in a TrustedSubsystem scenario. For a complete description of Trusted Subsystem pattern take a look at: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/WSSP.asp, and for an example of this in practice take a look at our WS-I BSP Sample Application for an example of this – http://msdn.microsoft.com/practices/guidetype/RefImp/default.aspx?pull=/library/en-us/dnpag2/html/MSWSIBSP.asp. .
The password is always sent as plaintext in the SOAP message. This option is recommended only when the UsernameToken is encrypted using a security token or certificate obtained from the target Web service, or when a transport protocol such as SSL is used to connect to the Web service. Otherwise, the password could be intercepted and compromised. WSE running on the recipient's computer compares the password in the SOAP message to the one returned from the AuthenticateToken method. If the two values are identical, the password is deemed valid.
WSE 3.0 supports protecting the password using the two standard security assertions – usernameForCertificate and usernameOverTransport. For more information on how this works take a look at our Direct Authentication pattern (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/WSSP.asp) and Implementing Direct Authentication pattern (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/wss_ch3_impdirectauth_wse30.asp).
From what I have seen this is the option that causes the most confusion. It is important to remember that this option is only intended to provide security on the wire – between two endpoints. It will not help you ensure your password is stored in a database in a hashed format! This is critical – see recommendation number 1. For more information on how to do this using AD, SQL or ADAM see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/wss_ch3_impdirectauth_wse30.asp.
If you are interested in better understanding this option – read on. I would also suggest thinking carefully about using this option in lieu of encryption as for the most part people’s passwords are low entropy and lend themselves nicely to offline guessing attacks. Keith Brown talks a lot more about this in his white paper http://msdn.microsoft.com/webservices/default.aspx?pull=/library/en-us/dnwse/html/securusernametoken.asp.
The hash of the password is sent in the SOAP message. When a SOAP message is received with a UsernameToken, WSE calls the AuthenticateToken method of a custom UsernameTokenManager class, which is registered in the configuration file. The AuthenticateToken method returns a password or password equivalent, which WSE creates a hash from. That hash is compared to the hash in the SOAP message and if they are identical, the hashed password is deemed valid.
The formula used to calculate the hash is: P_SHA1 (password, label + nonce + created) where:
● Password is either the real password or a password equivalent (for example, the hash of the real password) represented as a UTF-8 string.
● Label is a shared string between sender and receiver, also a UTF-8 string. For the WSE 2.0 Tech Preview, this is the string "WS-Security".
● Nonce is "Number Once" material, a random sequence of bytes generated from a cryptographic Random Number Generator (RNG), encoded as Base64.
● Created is the created time for the token specified in Zulu T[tbm1] ime using xsd:DateTime format, again encoded as a UTF-8 string.
In addition to verifying the hash, WSE also signs the message with HMAC-SHA1 using 16 bytes of key data from the hash keys.