Back to Blog
Python

Python bcrypt Salt and Password Authentication

python bcrypt salt and password authentication: Learn how to use bcrypt in Python for secure password hashing and authentication, including salt generation, verificati...

bcryptpassword hashingauthenticationsecurityPython
Illustration of a password being hashed with bcrypt, showing a salt being added and a secure hash output.

python bcrypt salt and password authentication requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

When storing passwords in a Python application, bcrypt is one of the most reliable choices because it handles salt generation automatically and provides a configurable work factor. This article covers how to use bcrypt for password hashing and authentication in Python, including the role of salt and the verification process.

Why bcrypt for Password Hashing in Python?

Storing plain-text passwords is unacceptable. Hashing with a fast algorithm like SHA-256 is also insufficient because attackers can use rainbow tables and brute-force attacks. bcrypt is a password hashing function designed to be slow and to incorporate a salt automatically. It uses the Blowfish cipher and has a configurable cost factor that increases the computational effort required to test each candidate password.

In Python, the bcrypt library provides a straightforward API for hashing and verifying passwords. It is widely used in web frameworks and authentication systems because it is well-tested and follows the principle of key stretching: the hashing process is intentionally resource-intensive, making offline attacks much harder.

How bcrypt Generates and Stores Salt

bcrypt does not require you to generate a separate salt. When you hash a password, bcrypt generates a random salt internally and embeds it in the resulting hash string. The hash string has a specific format: it starts with a $2b$ (or $2a$ or $2y$) prefix, followed by the cost factor, a 22-character salt, and a 31-character hash. For example:

$2b$12$LJ3mF6dH7xK9sQ2vZ0cEeO5yW1tN8qR4pB0sX2uY7iA1fG6hJ3k

The salt is stored as part of the hash, so you do not need a separate database column for it. This simplifies schema design and ensures that each password gets a unique salt even if two users choose the same password. When verifying, bcrypt extracts the salt from the stored hash and rehashes the provided password with that same salt, then compares the result.

Hashing a Password with bcrypt in Python

To hash a password, you use the bcrypt.hashpw() function. It takes the password as bytes and a salt as bytes. If you do not provide a salt, bcrypt can generate one for you using bcrypt.gensalt(). The typical pattern is:

import bcrypt password = b"my_secure_password" salt = bcrypt.gensalt() hashed = bcrypt.hashpw(password, salt)

The gensalt() function accepts a rounds parameter that determines the cost factor. The default is 12, which is a reasonable starting point. The higher the rounds, the slower the hashing, but the more resistant to brute-force attacks. You can store the hashed value (which is a bytes object) in your database as a string, typically by decoding it to UTF-8.

It is important to pass the password as bytes, not as a string. If you have a string, encode it with password.encode('utf-8'). The same applies to the salt if you are using a pre-generated one.

Verifying a Password with bcrypt in Python

During authentication, you receive a plain-text password from the user and need to compare it with the stored hash. Use bcrypt.checkpw():

import bcrypt stored_hash = b"$2b$12$LJ3mF6dH7xK9sQ2vZ0cEeO5yW1tN8qR4pB0sX2uY7iA1fG6hJ3k" candidate = b"user_provided_password" if bcrypt.checkpw(candidate, stored_hash): # password matches else: # authentication failed

checkpw() automatically extracts the salt and cost factor from the stored hash and performs the comparison. It returns a boolean. This function is designed to be constant-time to mitigate timing attacks, though the implementation details are handled by the library.

Choosing the Cost Factor for Your Application

The cost factor (also called rounds) determines how many iterations of the key derivation are performed. Each increment doubles the work. A higher cost factor makes hashing slower, which is good for security but can degrade user experience during login. You need to balance security and performance.

For a typical web application, a cost factor of 12 is common. If your server is powerful or you have high traffic, you might consider 13 or 14, but always test the actual latency. The cost factor should be chosen based on the hardware you run and the acceptable response time for login requests. It is also a good idea to periodically increase the cost factor as hardware improves, and to rehash existing passwords when users log in.

The bcrypt library allows you to specify the cost factor in gensalt(rounds=N). For example, bcrypt.gensalt(rounds=14).

Common Mistakes When Using bcrypt

One common mistake is using the same salt for multiple passwords. With bcrypt, you should never do this because gensalt() generates a unique salt each time. Another mistake is storing the salt separately; as we saw, bcrypt embeds it in the hash, so you should not strip it out.

Another issue is handling the bytes/string conversion incorrectly. Forgetting to encode the password before hashing will raise a TypeError. Also, some developers try to hash a password that is already hashed, which is not necessary and can lead to confusion.

Finally, do not use bcrypt for encrypting data; it is a one-way hash. If you need reversible encryption, use a different algorithm.

Password Authentication Flow in a Web Application

In a typical web application, you would hash the password when a user registers, and verify it when they log in. The stored hash is retrieved from the database and passed to checkpw(). It is important to handle the case where the stored hash is invalid or missing, and to use a constant-time comparison to avoid leaking information.

Here is a minimal example using a simple function:

import bcrypt def hash_password(plain_password: str) -> str: return bcrypt.hashpw(plain_password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') def verify_password(plain_password: str, hashed_password: str) -> bool: return bcrypt.checkpw(plain_password.encode('utf-8'), hashed_password.encode('utf-8'))

These functions can be used in your registration and login endpoints. Remember to always use HTTPS to protect the password in transit, and consider additional measures like rate limiting to prevent brute-force attacks.

python bcrypt salt and password authentication: Practical Us | RYUSLOG DEV