Cryptography – Post 2: Hashing Passwords — The Right Way

Learning Cryptography – Post 2: Hashing Passwords — The Right Way

Hashing is a powerful tool — but when it comes to storing passwords securely, it’s not enough by itself.

After digging deeper, here’s what I learned about the risks of using sha256(password) and how libraries like bcrypt solve those problems

Problem 1: Same input = same hash

Hash functions like SHA-256 are deterministic:

import hashlib

password = "123456"
hashed = hashlib.sha256(password.encode()).hexdigest()
print(hashed)

No matter how many times you run it, it will always return the same result.

This means:

  • Two users with the same password will have the same hash.
  • Attackers can use rainbow tables (precomputed hash dictionaries) to guess common passwords instantly.

Step 1: Add a Salt

A salt is a unique, random string added to the password before hashing:

salt = "g3n#x9v!"
combined = password + salt
hashed = hashlib.sha256(combined.encode()).hexdigest()

This makes each password hash unique — even if the password is the same.

But there’s a catch:
SHA-256 is still very fast — attackers can try millions of guesses per second.

Problem 2: Speed is a threat

Fast hash functions are great for integrity checks, but terrible for password security.

Why?

  • Attackers can try billions of hashes per day.
  • Even with salt, brute-force is still viable unless we slow things down.

Solution: Use bcrypt

bcrypt is a password hashing algorithm designed to be:

  • Slow on purpose
  • Resistant to brute-force attacks
  • Automatically salted
  • Cost-adjustable (you can define how slow it is)

bcrypt in Python

import bcrypt

password = "supersecret".encode()

# Hashing (salt is included internally)
hashed = bcrypt.hashpw(password, bcrypt.gensalt())

# Verifying
bcrypt.checkpw(password, hashed) # returns True

Every time you hash the same password, you get a different result — because bcrypt.gensalt() adds randomness.

Yet checkpw() still works because:

  • It extracts the salt from the stored hash
  • It rehashes the input using the same salt
  • It compares the results securely

What does a bcrypt hash contain?

Example:

$2b$12$KMnqWD9TcHOxI0n97Bz0s.8z9OAiK9AYIxB23zP1BdZZ3aqhtidXW
  • $2b$ → version
  • 12 → cost (2¹² = 4096 rounds)
  • rest → salt and hash

All the info needed to verify the password is inside this single string.

What I took away from this:

  • Hashing with SHA-256 is not enough for passwords.
  • Salting prevents rainbow table attacks.
  • But speed matters — hash functions should be slow.
  • bcrypt is purpose-built for password security.
  • One hash string contains everything needed: algorithm, salt, cost, digest.

Leave a Reply

Your email address will not be published. Required fields are marked *