Storing passwords securely is crucial for maintaining user privacy and data integrity. So, how should you store a password if you were asked to do so? The simplest way might seem to store it in plain text, but we all know that is not secure. Should we use encryption or hashing? Let's explore these methods together in our article today.
Plain Text Passwords
Alright, let's start with the most basic approach: plain text. Imagine you just write down all the passwords exactly as users enter them. It's simple, right? But here's the problem: if anyone gets access to this list, they can see all the passwords right away. It’s like leaving the keys to your house under the doormat. Not a good idea!
Encryption
Next, let's talk about encryption. Encryption is a process where we take a password and transform it into a different format using an algorithm. Think of it as a secret code. But here's the catch: encryption is reversible. If someone figures out the code (or gets the key, which you have to store somewhere), they can decrypt the password.
Experiment: Simple Encryption
Imagine we use something simple like ROT13, which just shifts each letter by 13 places in the alphabet. "password" becomes "cnffjbeq". But if someone knows we used ROT13, they can easily reverse it. Even with more complex encryption, if an attacker gets the key, they can decrypt all the passwords. So, reversible encryption isn't ideal for password storage.
Hash Functions
Now, let's move on to hash functions. Hashing takes a password and transforms it into a fixed-size string of characters, which looks nothing like the original password. The key here is that hashing is one-way: you can't easily reverse it to get the original password.
Experiment: Basic Hashing
Let's try using a simple hash function like SHA-256. Here's a quick example in Python:
import hashlib
password = "password123"
hash_object = hashlib.sha256(password.encode())
hashed_password = hash_object.hexdigest()
print(hashed_password)
You’ll get a long, unique string. But even with secure hashes like SHA-256, there's a problem: they are very fast to compute. An attacker can try billions of passwords per second.
Enhancing Security with Salt, Pepper, and Iteration
To make things harder for attackers, we can add some extra layers of security: salt, pepper, and iteration.
Salt
A salt is a unique random value added to each password before hashing. This ensures that even if two users have the same password, their hashed passwords will be different.
Experiment: Adding Salt
import os
salt = os.urandom(16) # Generate a random salt
password = "password123"
hash_object = hashlib.sha256(salt + password.encode())
hashed_password = hash_object.hexdigest()
print(hashed_password)
Now, each time you run this, you'll get a different result because the salt is random. This makes it much harder for attackers to use precomputed hash tables.
Pepper
A pepper is a common secret value added to all passwords. Unlike salt, the pepper is stored separately from the database. This adds an extra layer of security because an attacker would need both the database and the pepper to crack the passwords.
Iteration
Iteration means hashing the password multiple times. This makes each hash calculation slower, which significantly increases the time required for attackers to crack passwords.
Experiment: Adding Iteration
iterations = 100000
hash = hashlib.sha256((salt + password.encode())).hexdigest()
for _ in range(iterations - 1):
hash = hashlib.sha256(hash.encode()).hexdigest()
print(hash)
By increasing the number of iterations, you make it more computationally expensive for an attacker to guess passwords.
Using Specific Functions
Instead of creating our own hashing algorithms, we can use established functions designed specifically for password storage, like bcrypt, scrypt, and Argon2. These functions already incorporate salt, iteration, and other security measures to resist attacks effectively.
Bcrypt
Let's dive into bcrypt, one of the most popular password hashing functions. Bcrypt is based on the Blowfish cipher and is designed to be slow to compute, making it harder for attackers to crack passwords.
Experiment: Using Bcrypt
Here's how you can use bcrypt in Python:
import bcrypt
# Generate a salt and hash a password
salt = bcrypt.gensalt()
hashed_password = bcrypt.hashpw(b'password123', salt)
print(hashed_password)
# Verify a password
is_correct = bcrypt.checkpw(b'password123', hashed_password)
print(is_correct) # True
With bcrypt, each hash includes a unique salt, and you can adjust the "work factor" to make the hashing process slower, enhancing security.
Why Bcrypt is Effective
Bcrypt’s strength lies in its ability to adjust the cost factor, which means you can make the hashing process more computationally intensive as hardware improves. This future-proofs your password security, making it more resistant to attacks over time.
Famous Cases of Hash Cracking
To understand why all this matters, let's look at some real-world examples:
- LinkedIn (2012): LinkedIn used SHA-1 without salt, leading to the recovery of 90% of the leaked passwords in just three days.
- Adobe (2013): Encrypted passwords without proper hashing led to a massive breach affecting 38 million users.
- MySpace (2016): Use of unsalted SHA-1 hashes resulted in the compromise of 360 million accounts.
I hope this hands-on exploration helped you understand why and how to store passwords securely. Happy Coding!
Top comments (1)
Thank you