usable in any place a human can be used

20091215

plain stupid

[caption id="attachment_409" align="alignright" width="136" caption="typical dumbass"]typical dumbass[/caption]

Are you a dumbass? It's an important question to ask yourself every once and a while, especially if you want people to put their trust in you. This has been brought up because of a service called RockYou that recently exposed 32,603,388 plaintext passwords. Then it got even worse when it was confirmed that RockYou was also storing 3rd party passwords, again in plaintext.


Passwords are a tricky thing to store, RockYou clearly never got the memo. I've been at this whole "writing software thing" for a while and I've gone through my own personal journey of discovery about vulnerabilities and best practices. Let's walk through that now



  • Plaintext - This is the first stage, the one that RockYou never left apparently. It is the simplest to implement, take the password, shove it in the DB. Need to check against the password, if(input == password) and done! If you don't see the problem with storing your passwords plaintext, go to the nearest hard surface and slam your head into it over and over again, you no longer need cognitive functions.

  • MD5 Hashes - This is generally the second stage of password discovery. Many languages have a nice built-in function, php does. Store that MD5 hash and you get that warm fuzzy feeling in your tummy that you aren't storing things in plaintext. The problem now is that MD5 has well known weaknesses that can be exploited. Rainbow tables provide an attack vector and reverse look-up tools opens up a ton of exposure.

  • Salty passwords.... delicious - Your users are probably going to pick awful passwords, even if you have a password policy its probably going to end up "password1" You can't rely on them to provide strong passwords, so you can do the next best thing, make it strong for them. Static salting is the act of adding predictable data to a password before hashing. Maybe you append the string "Matt~is~the~best~programmer~7829" before hashing the string. This will defeat most reverse lookup tools (as very few people have bothered hashing "Matt~is~the~best~programmer~7829password1" and putting it into a database of reverse lookups) The problem is you are still open to dictionary and rainbow table attacks. If your database is compromised there is a chance that your source could be compromised as well, and then that string sitting in some configuration file is the key that unlocks everyone's password.

  • Dynamic Salt... deliciously nerdy - Instead of having a static string you can create some dynamic salt. When a user want to store a password, generate some dynamic salt, combine it with the password, hash, mangle, and store. Now every hash is a puzzle, security in layers.

  • Non-trivial calculation times - The problem with MD5 is that its too quick. Use a more complex hashing algorithm like SHA-1 or SHA-256, perform data mangling and multiple encryption sweeps so that turning a plaintext password into a hash you store takes some non-trivial amount of computation time. What's the point of this? It may add 1 second to saving and comparing hashes for you which is normally negligible in terms of user experience. The advantage comes from defending against a brute-force attack where the attacker has to generate millions of hashes. Will Bond does an excellent job outlining this in the rational of the fCryptography class in Flourish.

  • Timeouts - The normal user will have to enter a password once maybe twice if they fat-finger it. Using a quadratic-growth timeout system can help prevent brute-force attacks, or at least slow them way down. A brute-force attack relies on the ability to quickly cycle through thousands if not millions of passwords in an attempt to find the correct one. Let's take a look at how quadratic-growth timeouts defeat this system. Each attempt has a timeout twice as much as the timeout that preceded it, we will start with a timeout value of 1 second.

    • 1st Brute-Force attempt fails server let's the person retry, but they must wait 1 second.

    • 2nd attempt fails, retry in 2 seconds.

    • 3rd attempt fails, retry in 4 seconds.

    • 4th attempt fails, retry in 8 seconds.

    • 5th attempt fails, retry in 16 seconds.

    • ...

    • 10th attempt fails, retry in 512 seconds. (8 and a halfish minutes).

    • ...

    • 20th attempt fails, retry in 524288 seconds (8738 minutes or 145 hours or 6 days)


    Most users will never experience more that 3 seconds of timeout, a cracker would have to wait 35,702,051 years to attempt 50 passwords.


Storing passwords correctly and defending against brute-force attacks is non-trivial. It's one of those things that people a lot smarter than you or I have spent a lot of time thinking about. In a former life I was a mathematician (sometimes I fancy that I still am), I have had the pleasure of picking up a book on formal cryptography, getting about 2 chapters in, and setting it down with a headache. There are a lot of great libraries out there to do this stuff for you if you are too lazy to take the time to do it right yourself.


For php there is Will Bond's fCryptography, for perl there is Crypt (I'm not a perl person so anyone is more than welcome to provide a better alternative), for any language there is normally a high quality cryptography library. If you are not using them, then you are simply being lazy.


Of course you could side-step the whole issue and let someone else do your dirty work. Its all a symptom of a problem that Mozilla Labs is trying to solve, the concept of identity on the web. I think the future will be the browser being the trusted agent instead of third-party websites, but until then we have to be responsible software developers.

1 comment:

  1. Perfect! Nice job! So many people STILL don't get the point about storing passwords in plain-text.

    ReplyDelete