Cooking with credentials – the basics

The average online user today has over a hundred discrete online accounts to which they log in regularly. Unless those users are leveraging a password manager, the chances are high that they re-use a single password on several of these websites. As a result, users are trusting their online security to the level of protection afforded by each of these applications.

If an attacker breaches one application that is storing passwords in plaintext, that attacker now has a list of passwords that might be valid for other systems as well.

XKCD comic showing how hackers really leverage password breaches to steal online accounts.

While it’s easy to store passwords in plaintext, this is never the safe way to allow users to authenticate. Truly secure applications leverage one-way cryptographic hashes to ensure their users are protected. If the system is ever breached, the hashes stored within are unusable by the attacker on any other system.

Cryptographic Hashes

Longtime readers of php[architect] may remember my first foray into hashing in December 2018. In that article, we implemented a handful of hashing algorithms directly in PHP and discussed family secure hashing methods provided by Libsodium.

Each of these methods are secure ways to deterministically convert a known string of plaintext into a seemingly-random series of bytes that is otherwise indistinguistable from random noise. Given a specific hash, it is impossible to reverse to the original plaintext that generated it.

In the security world, we often talk about password cracking. This is the practice of starting with a target hash value and effectively guessing at the plaintext that generated it. Modern computers can make several thousand guesses per second to attempt to “crack” a password from a given hash. But this method is still “guess and check” and is not a true reversal of any secure hashing algorithm.

When a user signs up for your web application, it is best practice to store an identifier for that user (perhaps their email) and a hash of their plaintext password in your database. When the user chooses to re-authenticate with your system, they can again provide a plaintext password and your system can verify that password hashes to the value stored in the database.

If the hashes match, your user is authenticated. It’s simple, fast, and keeps your users secure because your system never stores the raw value of the users’ passwords.

The only problem with hashing is that, typically, two identical plaintext strings will hash to the same final value. What should we do to prevent leaking information if two separate users unknowingly use the same password?

Tune in tomorrow to learn how we prevent storing identical hashes in the database …