Dev Overflow – Part 3
By Mikhail Sudakov, Cyber Security Architect and Analyst, LEO Cyber Security.
Restating the main point of this entire blog series, security is about a human’s way of thinking and a human’s frame of mind – not about steel doors, firewalls, intrusion prevention, or intrusion detection systems. Although those tools will certainly help detect and mitigate (notice I didn’t even bother with “prevent”) breaches, security has always been and will always be about humans (unless SkyNet comes online and takes over at some point).
A2 – Broken Authentication
If Gandalf the Grey was in IT or security and implemented authentication protocols incorrectly or decided to roll his own session management, the adversary would most definitely pass and do so, in fact, fairly easily. “Application functions related to authentication and session management are often implemented incorrectly, allowing attackers to compromise passwords, keys, or session tokens, or to exploit other implementation flaws to assume other users’ identities temporarily or permanently.” – (OWASP). I’ll split this topic into two subsections: Authentication and Session Management. This post will start on authentication and the following one will finish that and go over my thoughts on server session management.
Many security experts would agree that passwords are a broken authentication mechanism, but we are stuck with them for the foreseeable future. It wouldn’t have been nearly as big of a problem if everyone just used max-length pseudo-random strings for passwords, stored them in a vault/manager, and used multi-factor where possible (*hint-hint*). Alas, the majority of users will often choose crappy passwords and, what’s even worse, reuse them on 2+ accounts. So, given that known factor, we need to implement a fairly safe authentication protocol. Should we roll our own?
The short answer is no. For most applications, there is not a good reason to implement your own authentication and password management. It is very easy to implement it very wrong. However, there are circumstances when we must roll our own. Hopefully not, but if we must, let’s take a closer look. Let’s meet Alice, Bob, and Charlie – three average users with multiple accounts and sub-par passwords. Alice’s key is “FluffyBunny1”; Bob’s is “LaserDragons!”; and, Charlie’s is “FluffyBunny1” (excluding all double-quotes).
I’ll waste no words on explaining why this is just… horrid. There is a special place in Dante’s Inferno for those who store passwords in plaintext (i.e. a place in a fire – a big one). I sincerely hope that all who store passwords know that they must be hashed. What’s a hash?
A cryptographic hash is a one-way sequence of mathematical operations on a string of characters of arbitrary length that outputs a fixed-length (which depends on a hashing algorithm) string. It is mathematically-impossible to reverse a hash into its original form because the sequence of math operations actually destroys data as it cycles through. However, it is also a mathematical certainty that each hash can be broken or cracked (it’s just a matter of time and effort). For instance, the hashed password “12345” will be broken in a few milliseconds while the hashed password “u^+44ye’cAfeTHB(Mh[W+0#fSX1y[d” will take a looooong time. Note that while a checksum may look sufficient for this purpose, it absolutely is NOT. A checksum is not designed to withstand a cryptanalytic attack.
OK, let’s pick a pretty good hashing algorithm that’s widely used today, like SHA-256. Take Two:
Oops… Houston, we have a problem! Did we pick a bad hashing algorithm? Nope, SHA-256 is perfectly fine for today’s use and has no known vulnerabilities. One of the properties of a hashing algorithm is that it is deterministic; it has to be – otherwise there is no verification of anything! Therefore, if string a == string c, it necessarily follows that Hash(a) == Hash(c). It may not seem bad at first, but this is a blunder. Just the fact that password hashes are the same leads the adversary to the conclusion that the underlying passwords have to be the same (which is exactly the case here).
This is an issue because according to the known fact, people use bad passwords and people reuse those bad passwords on multiple accounts. Even in the case that the underlying password is something like “u^+44ye’cAfeTHB(Mh[W+0#fSX1y[d“, by cracking one of the hashes, the adversary knows more than one underlying password. In effect, this makes the life of the adversary a breeze. More to the point though, it is more than likely that either Alice or Charlie had used the same “FluffyBunny1” password on another site. If that site had been compromised and the hashed passwords leaked and cracked, knowing one of the underlying cracked passwords gives the adversary access to them all. Not good, is it?
Somehow, we must make sure that the hashed values of the same underlying passwords are different. But, SHA-256 (like all crypto hashes) is deterministic – so how? Tune in to the next post where we’ll go over cryptographic Initialization Vectors (IVs), also commonly referred to as “salts”, to achieve that goal – and more. See how easy it is to blunder an implementation of authentication protocols? Unless we have no other choice, we should not roll our own.
To be continued in Part 4…