If you are building an application that requires users to register and log in, then you probably need to store their passwords somewhere. Doing this poorly can result in data being compromised and have many security flaws. Let's dive deep into different ways to store passwords in your database. We will look at different ways you can store passwords, their strengths and their weaknesses.
This is the most basic way to store passwords in a database. You treat passwords like normal data and store them in a normal column. This is a very bad way of storing passwords because if a hacker gains access to your database, everything is compromised. Database breaches happen way more often than we think.
- Easier to deal with with no overhead whatsoever.
- Not even a single level of security is used to prevent outsiders from accessing the stored passwords.
Encryption is the process of encoding information, which converts original information (in our case, plain text password) into an alternative form called cyphertext with a key. Encryption is a two-way function, which means if you have the key, you can convert cyphertext back to normal text. One example of encryption can be using Caesar Cipher. Before storing passwords in your database, you could encrypt them and store the cyphertext instead, which increases the security by a level compared to storing plain text passwords. But if an outside party gains access to your key, it's no different than storing passwords in plain text. Though if a hacker can access the database, do you think your key is safe?
- Secure than storing passwords in plain text.
- A third-party intruder has to deal with the additional effort to gain the key if your database is compromised.
- Intruders can decrypt the key and still gain access to the passwords.
Hashing is the process of mapping some data to some other representation using a hash function. While hashing and encryption sounds similar, they are not. Encryption works bidirectionally, while hashing is uni-directional (or assumed to be with our current understanding of mathematics). This means that provided a good hashing function, a hashed password cannot be converted back to a plain text password.
In this approach of storing passwords, we hash the passwords and then store what we call fingerprint (usually an alphanumeric gibberish) in the database. Every time the password needs to be validated, we hash the password that the user inputs and compare the fingerprints. User authentication's success is based on fingerprint matching, and if the prints do not match, the user's authentication is unsuccessful.
In some cases where we use a bad hash function, there can be hash collisions where two inputs provide the same fingerprint. This has, in fact, happened with older popular hash functions which claimed to be collision-resistant (MD5 and SHA-1). Maybe soon same thing will happen to our current popular hash functions.
This approach of storing passwords is also vulnerable to an attack called Dictionary Attack and Rainbow Tables. If our database is breached and someone gets access to the password hashes and the hash function used, then one could brute the passwords easily. A dictionary attack works by going through a list of text, hashing them and comparing the hashes to figure out the passwords. Lots of people tend to use generic passwords like "abcd123", "12345", "apple123", "letmein", etc., which can be easily cracked with dictionary attacks.
- Better than plain encryption.
- Once hashed, the passwords cannot be reversed.
- No more need for a "key" that allows anyone to decrypt the passwords in the database.
- Hash collisions can happen, which leaves room for vulnerability.
- Vulnerable to Dictionary Attacks, which can still lead to data breaches.
- Vulnerable to Rainbow Tables. Rainbow Table is a dictionary that contains a mapping from a fingerprint to plain text.
- If multiple users have the same passwords, their hashes would be the same, resulting in an easy breach.
Hashing and Salting
Rather than directly hashing the passwords, we can generate a random string called "salt" and prepend/append the salt in the password, then hash the altered string. We store the salt in the database. When the user tries to authenticate, we can do the same and hash the password and compare the fingerprint-like you would do in normal hashing. Note that you will need to use the same salt and same pending method every time you need to verify the password since altering them would generate a different fingerprint than the original one. This can prevent rainbow table attacks, but you are still prone to dictionary attacks as salt is stored in one of the databases that will be breached if a breach happens.
- Prevents against rainbow tables
- Outsider cannot distinguish if multiple users have the same password
- Still vulnerable to dictionary attacks
In this method, we use dedicated password hashing functions, which are slower than normal. Some of the slow hashing functions are bcrypt, Argon2, and scrypt. These functions are deliberately slow so that an outsider would take a significantly longer time trying to hash during a dictionary attack. Normal hash functions are very fast and, with the power of specialized hardware, can compute hundreds of gigahashes per second. Slow hashing tries to prevent this issue. You can also choose the level of slowness you would want with these dedicated hashing functions called work factor. In case of a breach, weak passwords still can be easily breached, but if you have a complex password such as "HB3Ah29qEK6ziTZZodLzmoZW5JEvNT&CfgkB!%23gDE", a hacker would have a hard time breaking it.
- Harder for hackers to crack the passwords because it takes a longer time.
- They are still not immune to dictionary attacks.
Don't Store Passwords at All
Ultimately, you can use other authentication providers, such as Auth0, Google, Facebook, etc., for the users to authenticate to your application. This reduces the hassle of storing passwords in your database because you wouldn't be storing any. It is also convenient for the users as they would have to remember one less password.
Storing passwords in databases is a much more complicated task than one could think. Storing it in plain text, the password is wide open, encrypted passwords are still recoverable, hashing is vulnerable to precomputed rainbow tables, hashing+salting is vulnerable to specialized hardware dedicated to cracking such passwords such as GPUs, and slow hashing just slows down the attack and is not a complete solution.
In the end, storing passwords in your database, no matter what you do, would still be vulnerable in some way. The best bet would be to go with third-party authentication providers like Google, Facebook, Auth0, etc.
Thank you for reading this article. If you loved it, please consider subscribing and leaving a response in the comment section below.