Building a product that requires passwords for user authentication means that you need to implement a method to verify your users’ passwords. However, storing plaintext passwords in your database comes with significant security risks.
Holding this data opens your app up to risk if your database gets breached. Hackers — who can be rogue employees that have access to the database — benefit from these attacks because of the prevalence of users re-using the same password across different websites. Even if your application isn’t particularly security sensitive, attackers hope your users have used those passwords at higher value accounts like their bank accounts. Securely storing passwords is essential to mitigate these risks.
Password hashing is a strategy to ensure that passwords are stored securely. In this blog post, we’ll explain what hashing is, why it’s important, and how Stytch has implemented it to make our Passwords product as modern and secure as possible.
The basics of password hashing
Password hashing is the practice of algorithmically turning a password into ciphertext, or an irreversibly obfuscated version of itself, as a means of blocking against the threat of password breaches. Essentially, one string of characters in a password is transformed into a completely different string using a mathematical hashing function. Once a string (password) has been hashed, there’s no way to reverse the process and each time a user logs in, their hashed password is compared with a recorded hashed value.
That might sound a bit technical, so let’s put hashing into human terms — say you have two users, Alice and Bob, who create an account on your website or platform. When they finish registering with a username/email and password, you store their passwords in a database.
When you’re storing these passwords, the worst thing you could do is store them in plain text. Storing in plain text means you’re directly tying your user’s identity to their password in a readable format, as seen in the image below:
Even if the database is itself secured by authentication, this storage mechanism remains highly vulnerable. If a hacker is able to break into the server, they’ll have access to all of the application’s password credentials without clearing any additional hurdles. This endangers both the hacked application and any applications where that user has re-used the password.
With this information in tow, bad actors can take these login credentials and use them across websites other than your own. Although it’s a poor practice to use the same password for multiple accounts and across multiple platforms, over half of us still do. And if user passwords are exposed and continuously exploited from a breach stemming from your website or platform, your users could ultimately hold your organization responsible, which can have reputational, financial, and legal consequences.
It would be a mistake to believe that the remedy to storing passwords in plain text is to simply use encryption. There’s no doubt that compared to plain text, encrypting the passwords is an improvement. If a hacker gets access to the encrypted passwords, they won’t be able to use them immediately, as the encrypted passwords are harder to crack and represent a randomized alphanumeric string, as in the image below.
You might be safe from hackers for a while using encryption, but there’s a caveat: encryption is a two-way street, meaning that the encryption key can be used to decrypt the passwords back to plain text.
If hackers infiltrate your database, they could potentially break into other parts of your network and discover your encryption keys. Storing encrypted passwords also makes them vulnerable to any malicious employee with privileged access to the keys.
How is hashing different from encryption?
Unlike encryption, hashing is a one-way street. When you run a user’s passwords against a one-way hashing algorithm, the result is a random string of text that cannot be reverse engineered or traced back to the original plain text password value.
How does it work? When a user creates a password, the application runs it through a hashing algorithm and stores that random string value in their database (vs. storing a plain text password or an encrypted value). Any time a user logs in, the application runs the same hash function against the user’s submitted plain text value to check if the resulting random string is the same random string stored in its database.
The irreversibility of this hashing function is what makes it such a powerful way to securely store secrets like passwords. It removes the most susceptible attack vectors.
Consequently, even if your hacker discovers the keys used to generate the hash, they won’t be able to convert the hashed values back into the original passwords.
However, this doesn’t mean it’s impossible to crack hashed passwords. Some cyber attacks, such as brute force attacks, dictionary attacks, and rainbow tables can help malicious individuals steal passwords.
In a brute force attack, a huge number of character combinations are used in the password guesses. In the dictionary attack, only words from a long dictionary are tested. Dictionary and brute force attacks have low memory requirements but are computationally expensive because they have to hash the entire list every time.
Rainbow tables are a more computationally efficient (but also more space intensive) way that hashed passwords can be cracked. In this approach, a rainbow table is created that stores a series of chained hashes for common passwords. The hacker can then use the table as a lookup, to see if the compromised database password hash exists in the table and then trace that hash chain backwards to identify the original plaintext password.
Adding salt to hashing
As you can see, hashing alone isn’t secure enough. To better protect passwords from attacks, you add something called a salt. A salt is a sequence of additional characters that we combine with the password. This combination goes through the hash algorithm to create the password key, as demonstrated below:
While salting is a helpful step in securing hashed passwords, it has limitations. For instance, this salt is often placed in front of each password, creating a trend that’s easy for hackers to identify. Moreover, some organizations will use the same salt for every password, which inherently undercuts the security that salting offers.
The fast hashing problem
While being “fast” is almost always a desired quality in technology, fast execution is a big problem in hashing algorithms because hackers can use speed to their advantage.
Some hash algorithms, including MD5 and SHA1, are quite fast. And while those algorithms have been used for hashing passwords and can be helpful for some everyday tasks, like database partitioning or computing checksums, security advisors now strongly disapprove of them — especially for password hashing. Modern hardware, such as GPUs, have hundreds of cores that enable massively parallel computing and allow hackers to compute billions of hashes per second when attacking fast hashes.
You need slower hash algorithms to protect users from hackers using ultra-fast GPUs. Examples of these include Argon2, bcrypt and Scrypt. In the next sections we’ll dive deeper into the latter two, which we explored for Stytch’s Passwords solution.
bcrypt is a round-based, adaptive hash algorithm to store a one-way hash of the password. It improves on regular salting by generating a random salt for each password.
With powerful hardware, a hacker can create an attack dictionary against fast hashing algorithms like md5 in a few seconds. But since bcrypt is a slow algorithm, creating this dictionary will take a long time. Additionally, you can increase the iteration count to make it even slower, which helps protect you against brute force attacks.
This is a typical bcrypt hash:
The default cost (rounds) of bcrypt is 10. When this algorithm was conceived, the cost of 10 was secure enough. However, depending on the hardware, this now can take milliseconds. So, you can increase the cost of rounds to 15 or more to make the hashing process take several seconds instead.
Scrypt, also known as Scrypt PBKDF2, was initially developed for Tarsnap, an online backup service with Amazon as one of its clients. Scrypt is a password-based key derivation function (PBKDF) — a memory-intensive algorithm that makes it costly to perform large-scale hardware attacks.
Unlike other PBKDFs, Scrypt’s parallelization factor enables you to finetune the relative CPU cost. The desired key length (dkLen) allows you to define the output size and the CPU and memory cost, further bolstering its security profile.
The first step when using Scrypt is to create a hashed message authentication code (HMAC). Then, Scrypt performs the PBKDF2 function. A memory-hard function called SMIX is executed in a loop, and finally, a PBKDF2 function is executed again. The SMIX function depends on three functions: ROMix, BlockMix, and Salsa20.
Scrypt follows the steps outlined below to hash passwords:
- First, Scrypt generates an expensive salt using the block size factor (the r parameter in Nrp) to create “blocks.”
- PBKDF2 generates a large array whose size depends on the block size factor (r) and parallelization (p).
- Scrypt uses the cost factor (N) as the number of times that each block is mixed using a function called ROMix.
- Scrypt then generates a new expensive salt by concatenating all mixed blocks.
- Finally, PBKDF2 uses the password and the calculated salt, trimming the output to the desired number of bytes (dkLen).
Stytch’s Passwords solution
There are some security considerations to think through whenever you’re storing a secret value like a password. Although a hashed value can’t be directly translated into a password, its formatting is susceptible to different forms of security breaches, like brute force attacks, where bad actors can continuously generate hashes from passwords until they find one that matches.
One approach to avoid these headaches altogether is to pursue a passwordless-first approach, but we understand that not everyone is ready to drop passwords entirely. For some user demographics, passwords remain the preferred way to authenticate. That’s why in addition to our suite of passwordless authentication methods, Stytch offers customers a modern approach to securely allowing users to authenticate with Passwords.
Stytch salts and hashes all passwords using Scrypt, before storing in an encrypted database that we manage. We wanted to ensure our Passwords solution is secure and built for performance — we decided on Scrypt in order to strike that balance.
Stytch’s Passwords also introduces novel security features like breach detection (powered by tools like HaveIBeenPwned) and a better strength assessment called zxcvbn (pronounced lower qwerty), which makes it easy for humans to generate passwords but hard for robots to crack them. We’re committed to making secure authentication that’s as frictionless as possible, so we leverage our Email Magic Link technology to reduce the steps from a traditional password reset.
Get started with Stytch
Not all companies are ready to go passwordless, but when ready, they can easily migrate from password-based to passwordless with Stytch. In the meantime, they can rely on our solutions to enjoy the best of both worlds.