Dev Overflow – Part 4
By Mikhail Sudakov, Cyber Security Architect and Analyst, LEO Cyber Security.
This post is a direct continuation of my previous post on broken authentication and session management, which was split into two parts. You might want to review Part 3 (Back to Part 3) before proceeding.
A2 – Broken Authentication (Cont.)
To recap briefly, we should not roll our own authentication protocols in general. But if we must, here we are considering some common mistakes. We have 3 users: Alice, Bob, and Charlie; and, their respective passwords are “FluffyBunny1”, “LaserDragons!”, and “FluffyBunny1” (yes, Alice’s and Charlie’s are the same). We have already observed that storing the passwords in plaintext is the greatest mistake of them all. And simply using a hashing algorithm (like SHA-256) on a password itself is not nearly good enough because every hashing algorithm has to be deterministic for verification to succeed; therefore, it is obvious that if two users have the same password (like Alice and Charlie), and this happens all the time, their hashes would be the same. That gives the adversary too much power and is an absolute no-go. In addition, it makes attacks like using Rainbow Tables a breeze.
Lastly, I mentioned that what we have to implement into our protocol is something called a cryptographic Initialization Vector (IV) that is known as a “salt“. I strongly dislike the term “salt” for at least a couple of reasons, but I’ll use it here to keep math terms to a minimum. The salt is a cryptographically “pseudo-random” (will just call it “random” from now on) sequence of bytes that we prepend to a password before hashing with our SHA-256. First, it differentiates the same passwords of multiple users into completely different hashed values. But more importantly, it greatly slows down attacks like rainbow tables to crack stolen passwords. Remember that we are not trying to prevent breaches – they are a question of when, not if.
Alright, let’s try to generate a good salt to prepend to a password (I’m using C# syntax). For example:
Random rnd = new Random(); Byte b = new Byte; rnd.NextBytes(b);
Right? WRONG!!! As stated before, it is incredibly easy to completely mess this up and shoot oneself in the foot – which is exactly what we’ve done in the code lines above… If you are not a developer, this may come as a surprise (if you are one, it better not), but when you call a simple “random” method/function/subroutine in most frameworks, what you get is not really random and it must not be used in any crypto operation. In reality, good randomness is quite a commodity in computing and is not easy to come by. For the vast majority of randomness needs in computing, the simple “random” method will work. However, not for security. Never for security!
A more appropriate example in the same .NET framework would be:
using System.Security.Cryptography; ... RandomNumberGenerator rng = new RNGCryptoServiceProvider();
Whatever the framework is, we must engage the crypto service library to generate randomness/entropy/chaos. Entropy is extremely important in cryptography and cannot be overlooked when generating salts for password hashing. With bad entropy, we get blunders like 802.11b WEP… Remember that little gem? On some 802.11 cards then, the initialization vector was reset to zero after a power cycle!
What we’ll do is prepend the random salt bytes to each password and then hash with SHA-256, like so: hash(salt + password). This is what we get:
OK, the password itself has now been salted and hashed, but you might be wondering why we simply put the plaintext bytes of the salt we used into the same database table. After all, we assume that the adversary will steal it at some point. Shouldn’t we obfuscate the salt somehow? Well, it so happens that we don’t need to – it’s kind of mathematical magic. Great! It looks like we are all set with secure password storage and authentication. Right?
Alas, WRONG once again. This is where I will stop because it’s about to get pretty involved, and I want to keep to the surface in this blog series. To proceed from here, we must use a keyed-hash stretcher like HMAC-SHA-256 to make it computationally-difficult for the adversary to break the protocol, but the topics of keyed hashes and secure crypto key management are quite a bit beyond our scope here. My main point remains: unless you have no other choice, don’t roll your own. If you must, then may your computations be accurate, your entropy strong, and your logic ironclad. If you would like to read more on this, Sophos’s Naked Security has a great article on password management. And of course, more information about broken authentication and session management can be found at OWASP Top 10.
Like with authentication, you should not roll your own. But, if you must:
DON’T. I’m serious. If you thought authentication was a mouthful, this is way worse.
In the next post, we will take a look at OWASP A3 – Sensitive Data Exposure.