Skip to main content

How an audit can make you less secure

· 5 min read
Alejandro Revilla
jPOS project founder

First a disclaimer: I know excellent auditors, starting with my friend Dave from the Payments Systems Blog, but I also know really retarded ones, and here is a little story of a system I built some 8 or 10 years ago that would have been resilient to the HeartBleed bug, but of course, the auditor couldn't understand it, and it had the word MD5 which puts them to cry like tiny little girls, so we have to "improve" it to make it less secure.

HeartBleed

I've been into amateur packet radio and BBS systems in the 80s where monitoring the air, or a serial line was easy, so things like one time passwords and two factor authentication have been always within my area of interest. When it came to provide an internal user interface to jPOS, and I had to design a login form, I wanted to protect the user's passwords against an operator with access to the system; I wanted people to be able to use a password like TheBossIsAnIdiot if they wanted, making it difficult for the programmer/operator on the server side to see it.

So the solution was easy. The server would generate a nonce and send it to the client, the client would use that nonce, some other data (like the session id) and the password, and send an MD5 of all that to the server.

I wasn't and I'm not a JS expert, and we didn't have things like jQuery or Angular those days, but I wrote this little piece of code that implemented the login form:

Login Form

    function doHash(frm) {
var username = frm.username.value;
var password = frm.password.value;

if (username.length < 3 || password.length < 3) {
alert ("Invalid Username and/or Password.");
return false;
}
var hash = frm.hash.value;
var seed = readCookie ("JSESSIONID") + hash;
var pass = hex_md5 (username + password);

frm.password.readOnly = true;
frm.password.value = hex_md5 (seed + pass);
}

The server would do the same computation in order to verify the login.

I wasn't comfortable with the solution, because somehow the initial password was either entered by the operator, or sent via email, so I forced a password change in the first login, and the password change would just send an XOR of the existing password hash so that the server could apply the same XOR and upgrade the password to the latest version.

But here comes the auditor, with a bucket in his head, and reports the process as insecure (with strong copy/pasted wording to scare management) for the following reasons:

  1. On password change, the complexity of the password (you know, password length, use lower and upper and all that crap) is validated in client side, not in the server side. And there goes a rant that says "Passwords should be at least XXX characters, and have lower case and upper case letters in order to be secure, yada yada yada", so the manager would look at me like "We trusted in you... look what you did to us, our passwords can be less than XXX characters if the user hacks the client side code!").

  2. It has the word MD5 and we just heard that MD5 is broken (remember this was 2005~2006), and there goes the rant about how MD5 was recently cracked, and again, the manager would give you that look, like saying, you're a lost case, I've been always scared of open source and the freetards around it.

I think the tradeoff between having one user hacking the JS to force himself a weak password, compared to protecting all users from easy eavesdropping is a good one. I also think that sending an MD5 over the wire is better than sending a clear password (although there's of course SSL involved, I'm talking about 'clear' from an application perspective). It has the side benefit of staying secure while in memory on the server side.

Auditors are obsessed with the things they were told to look after, SQL injection, XSS, or things they test with automated tools. That's fine and welcome (you don't need an auditor for that, BTW, but it's good to have more eyes on the problem). But I've never seen an auditor testing to SQL inject say a field 35/45 in an ISO8583 message, something anyone can do forging the track2 of a card and going to a shop around the corner. Take what they say with a grain of salt, and remember not all, but most of them, are just talkers.

I'm playing with the idea of making the client perform a really large number of iterations on the hash, to slow it down (kind of a client side bcrypt) without requiring too much CPU on the server, and then have the server do some more rounds, perhaps with a bcrypt final pass. I'm planning to send some timing information to the server too, in order to alert on client hardware/software changes (how long did it take you to run 100K hashes?). We'll have to figure out how to explain this in our next audit..