Skip to Content
Course content

261: Password Hashing with bcrypt

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're running a high-security vault. Instead of keeping a list of every customer's actual key, which would be a disaster if a thief stole your list, you use a specialized meat grinder. When a customer creates a key, you run it through the grinder. You don't keep the key; you only keep the resulting pile of ground-up metal. Now, when that customer comes back, they give you their key again, you run it through the same grinder, and if the new pile of metal looks exactly like the old pile, you know they have the right key.

Crucially, no matter how hard a thief tries, they can't "un-grind" that metal to figure out what the original key looked like. That's the core of password hashing.

In Python, we use bcrypt for this. It doesn't just "grind" the password; it adds a "pinch of salt" (random data) so that two people with the same password don't end up with the same hash. It also lets us decide how slow the grinder is, which makes it incredibly expensive for hackers to try billions of guesses per second.

Turning Passwords into Unreadable Hashes

First, you'll need the library: pip install bcrypt. One thing that trips people up immediately is that bcrypt doesn't want strings; it wants bytes. You have to encode your password before passing it in.

import bcrypt

# This is what the user provides during sign-up
password = "SuperSecretPassword123".encode('utf-8')

# gensalt() handles the randomness for us automatically
# It also embeds the 'cost factor' (how slow the hash is)
salt = bcrypt.gensalt()

# Now we hash it
hashed = bcrypt.hashpw(password, salt)

print(hashed) 
# You'll see a long string of bytes starting with $2b$

I want you to notice that we don't actually need to store the salt in a separate database column. bcrypt is clever—it bundles the salt and the cost factor directly into the resulting hash string. When it's time to verify the password, the library just reads the salt back out of the hash itself.

The Verification Process

You'll never "decrypt" a bcrypt hash because that's mathematically impossible. Instead, you take the password the user just typed in and check if it produces the same hash as the one you have stored.

# Let's assume 'hashed' is what we pulled from our database
user_input = "SuperSecretPassword123".encode('utf-8')

if bcrypt.checkpw(user_input, hashed):
    print("Login successful!")
else:
    print("Wrong password, buddy.")

Slowing Down the Attacker

The "cost factor" is where bcrypt really beats older methods like SHA-256. If you look at bcrypt.gensalt(rounds=12), that rounds parameter determines how many iterations the hashing algorithm goes through.

Every time you increase that number by one, you double the time it takes to hash the password. To a human, 0.3 seconds versus 0.6 seconds is invisible. But to a hacker trying to crack a million passwords a second, that delay is a brick wall. As computers get faster over the years, you can just bump up the rounds to keep your users safe without changing your entire codebase.




📋 Practical Task

Build a Secure User Credential Store

Write a small Python program that simulates a user registration and login system. Your code should implement the following:

  • A dictionary called user_db where the keys are usernames and the values are the hashed passwords.
  • A function register_user(username, password) that encodes the password, hashes it using bcrypt, and saves it to the dictionary.
  • A function login_user(username, password) that retrieves the hash from the dictionary and uses bcrypt.checkpw to verify the password. It should return True if the login is successful and False otherwise.

Test your system by registering a user, attempting to log in with the wrong password (expect False), and then logging in with the correct password (expect True).

Rating
0 0

There are no comments for now.

to be the first to leave a comment.