Adam Caudill

Security Leader, Researcher, Developer, Writer, & Photographer

Hash Storage: Make Attackers Work

So you hash your passwords? Good. Do you salt? That’s good. Do you use a strong hashing algorithm (PBKDF2/bcrypt/scrypt)? Great! But how do you store the hashes? What happens when you get hit with a SQL injection attack?

I’m a big believer in defense in-depth – not that marketing garbage about stacking layers of blinky-light boxes, but using techniques to add extra work for an attacker. You might not be able to stop every attack, but the more work they have to do, the better the odds they won’t get everything they want.

So, let me describe a scenario:

$company stores passwords as SHA1(password + salt) hashes (terrible, I know – but still common). They install a new web application that connects to their main database – this new application has a previously undisclosed flaw, SQL injection.

$evil_hacker finds this flaw and looks for companies that use the application; finds $company, exploits, dumps their user table and posts the hashes on PasteBin. Having both the hash and salt, those hashes will fall_quickly_.

We’ve all seen it before – far too many times.

Now, let me modify the scenario slightly: instead of SHA1(password + salt), make it AES128(salt : SHA1(password + salt), key) – where key is an application secret.

Now, we have a different story – as long as the application secret remains secret, the hashes are safe. Stored in the clear, they will fall quickly – encrypted, they are safe as long as the secret remains safe.

In the case of many SQL injection attacks, the attacker is able to access the data, but doesn’t have access to the application source code. In these cases, you can add a few extra milliseconds to processing time and as a result stop attackers from using stolen data from an entire class of attacks. There’s nothing new about this idea – but I don’t see it used nearly often enough.

Obviously this doesn’t help if the attacker is able to retrieve the source code, so you still need to use a strong hashing algorithm. Encrypting a strong hash buys you extra protection against certain attacks, and maintains a strong baseline against others – at a very small performance cost.

In case of a breach, I would still assume that the attacker knows the encryption key, and reset passwords accordingly. Seeing as it takes time to find out the extent of the breach, it’s safest to act as if the attacker has everything and limit the damage as quickly as possible. In reality though, the odds of the passwords being exposed as a result of a SQL injection attack is reduced significantly thanks to the added encryption layer.

Storing the data like this keeps everything secure, and in a single field (and makes it easy to integrate into existing applications):

<IV>:AES128(<salt>:HASH(<password><salt>))

This way all of the material needed is encrypted; though it could be argued that just encrypting the hash or the salt would be sufficient (assuming the salt is at least 16 bytes and generated by a CSPRNG). The core idea here is to deny the attacker of as much information as possible; making it as difficult as possible to find passwords.

There are no silver bullets, but we can take advantage of the tools we have to make attackers work harder to get our secrets.

Adam Caudill


Related Posts

  • When Hashing isn’t Hashing

    Anyone working in application security has found themselves saying something like this a thousand times: “always hash passwords with a secure password hashing function.” I’ve said this phrase at nearly all of the developer events I’ve spoken at, it’s become a mantra of sorts for many of us that try to improve the security of applications. We tell developers to hash passwords, then we have to qualify it to explain that it isn’t normal hashing.

  • Ruby + GCM Nonce Reuse: When your language sets you up to fail…

    A couple hours ago, Mike Santillana posted to oss-security about a rather interesting find in Ruby’s OpenSSL library; in this case, the flaw is subtle – so much so that it’s unlikely that anyone would notice it, and it’s a matter of a seemingly insignificant choice that determines if your code is affected. When performing AES-GCM encryption, if you set the key first, then the IV, and you are fine – set the IV first, you’re in trouble.

  • Password Hashing: No Silver Bullets

    In the dark days of the web, if a service hashed your password instead of storing it in plain text, they were doing good. As sites were hacked, and credentials stolen, a silver bullet emerged: always hash and salt passwords when storing them. Many, many services were built with this design – LivingSocial is a great example. SHA1 hashing with a 40 byte salt – once upon a time, that was considered reasonable protection.

  • 1Password, PBKDF2, & Implementation Flaws

    …or “Crypto Is Hard, Vol. 479” Earlier today a tweet about a new feature for oclHashcat-plus started a truly interesting debate on Twitter over the implications. The new feature is the ability to crack 1Password keychain files – at an impressive 3 million passwords per second. Support added to crack 1Password to oclHashcat-plus, 100% computed on GPU! Plus I found an exploitable design flaw http://t.co/53ZtWggsDz — hashcat (@hashcat) April 16, 2013 To achieve this speed, two optimizations were used – the first is in precomputing ipad and opad for SHA1-HMAC, this effectively cuts the number of SHA1 calls in half.

  • Win by Building for Failure

    Systems fail; it doesn’t matter what the system is. Something will fail sooner or later. When you design a system, are you focused on the happy path, or are you building with the possibility of failure in mind? If you suffered a data breach tomorrow, what would the impact be? Does the system prevent loss by design, or does it just fall apart? Can you easily minimize loss and damage, or would an attacker have free rein once they get in?