September 24, 2007 12:00pm
I was excited to learn recently that the Nokia N73 can speak AtomPub, and that a friend of mine owns one. I thought I’d try to make it talk to the new AtomPub implementation in WordPress, but reading through the N73 documentation I found that it only supports WSSE authentication, and WordPress only speaks HTTP Basic Authentication. I’d never heard of WSSE, but Mark Pilgrim has a good write up on XML.com, and the Ape has the ability to speak WSSE, so I thought I’d implement it in WordPress. Bear in mind that I’m not writing this from a security point of view, I’m just looking at authentication as a necessary evil to get cool AtomPub things working. And there’s a spoiler: it can’t be done :)
A WSSE client will send an Authorization header which, as we know, will get dropped if Apache is passing the request off to a CGI, and a X-WSSE header that looks like this:
X-WSSE: UsernameToken Username="USERNAME", PasswordDigest="PASSWORDDIGEST", Nonce="NONCE", Created="2007-09-08T05:52:36Z"
PasswordDigest
is a base64 encoded SHA1 digest of the concatenation of the nonce, the timestamp and the password. The nonce is of course some random string.
So, to add WSSE into WordPress AtomPub, we can add some code to the authentication function in wp-app.php.
First, we check if the client is trying to authenticate using WSSE by looking for a X-WSSE header.
if(isset($username_token = $_SERVER['HTTP_X_WSSE'])) {
We then take the Username Token contained therein and split out the user, digest, nonce, created information sent by the client. There are probably nicer ways to do this.
$wsse = array( 'user' => "", 'digest' => "", 'nonce' => "",
'created' => "", 'password' => "");
$tokens = explode(", ", trim(strstr(stripslashes($username_token), " ")));
foreach ($tokens as $token) {
$pivot = strpos($token, '=');
$key = substr($token, 0, $pivot);
$value = trim(substr($token, $pivot + 1), '"');
switch ($key) {
case "Username":
$wsse['user'] = $value;
break;
case "PasswordDigest":
$wsse['digest'] = $value;
break;
case "Nonce":
$wsse['nonce'] = $value;
break;
case "Created":
$wsse['created'] = $value;
break;
}
}
Finally, we recreate the digest on the server, and compare it to what was sent, and close the if.
$wsse['password'] = get_password_by_login($wsse['user']);
$server_digest = base64['encode(pack("H*", sha1($wsse['nonce'] . $wsse['created'] . $wsse['password'])));
if ($server_digest == $wsse['digest']) {
$login_data = array('login' => $wsse['user'], 'password' => $wsse['password']);
}
}
If you have familiarity with WordPress’s code, you might be saying something like, “WTF is this get_password_by_login() function call? I’ve never seen such a thing!” Good question. And the dirty little secret is that no such function exists. A weakness of the WSSE authentication scheme appears to be that to recalculate the digest the password needs to be stored in plain text on the server. This is probably at least as bad as sending the password in plain text over the wire, the thing that WSSE is trying to avoid. WordPress, sensibly, does not store passwords in plain text, but computes an md5 hash of them and stores that.
So, as far as I can tell, there is no way to implement WSSE in WordPress in any sensible way.
One little word on security. If we could implement WSSE, the code should keep track of nonces and make sure they aren’t repeated, and should reject UsernameTokens created more than a couple of minutes ago (leaving aside any discussion of synchronisation of your client’s clock with my server).
P.S. I hadn’t read Joe Cheng’s comment or Joseph Scott’s reply in the comments of the post I linked to above before I started off on this wild goose chase.
September 24th, 2007 at 3:12pm
What about storing an encrypted version of the password, and then decrypt it, using something like http://au3.php.net/manual/en/function.mcrypt-encrypt.php?
You still have to store the key in plaintext somewhere in your app, but you still have to do this with username/password to access the database anyway, so as long as your server doesn’t get hacked into (by the cat, for example, who has direct access to the terminal and a lot of time on her hands….)
September 24th, 2007 at 3:23pm
True, you could use round trippable safe password storage. That would require a breaking change to WordPress authentication, which we’re not likely to see implemented (or accepted).
October 19th, 2007 at 10:44am
[…] WSSE Authentication, such as the Nokia N73. Solution: As a proof of concept in the past, I implemented WSSE in WordPress. One slight catch, and the reason it didn’t work in WordPress, is that WSSE requires plain […]