Ensuring user data security is one of the most critical responsibilities of any software developer. When it comes to protecting credentials, the golden rule is to never store passwords in plain text. Instead, we use Python password hashing to transform sensitive information into an unrecognizable, irreversible sequence of characters. If a database is breached, the attacker finds only complex cryptographic codes instead of real passwords. This article teaches you how to implement a robust security system using best practices.
What is a hash and why is it essential?
A hash is the output of an algorithm that transforms any block of data into a fixed-length sequence. Unlike regular encryption, hashing is a one-way operation: you cannot “decrypt” a hash to get the original password back. When a user logs in, the system hashes the entered password and compares it to the stored hash. According to OWASP, secure password storage is the first line of defense against large-scale data breaches.
Choosing the right library
Python’s built-in hashlib with MD5 or SHA-256 is not recommended for passwords. These algorithms are too fast, allowing attackers to test millions of combinations per second. Use algorithms designed to be intentionally slow, like bcrypt or Argon2. Bcrypt is an industry standard that automatically handles the “salt” (random characters added to the password before hashing, ensuring two users with the same password get different hashes). For generating cryptographically secure tokens, see also Python secrets module.
pip install bcryptGenerating a secure hash
import bcryptVerifying a password
def verify_password(entered_password, stored_hash):Production-ready function with configurable cost
The rounds parameter in gensalt() controls the computational cost. Higher values make hashing slower, which is good for security but has a performance cost. The default is 12. OWASP recommends tuning this so hashing takes around 1 second on your server. For a complete project applying these concepts, check out building a password manager in Python.
import bcryptMD5/SHA-256 vs bcrypt: comparison
| Algorithm | Speed | Salt | Recommended for passwords |
|---|---|---|---|
| MD5 | Very fast | No | No |
| SHA-256 | Fast | No | No |
| bcrypt | Configurable (slow) | Yes (auto) | Yes |
| Argon2 | Configurable (slow) | Yes (auto) | Yes (preferred) |
Frequently asked questions
Can I recover the original password from a bcrypt hash?
No. Bcrypt is a one-way function by design. There is no decryption. The only way to verify a password is to hash the new input and compare it to the stored hash using bcrypt.checkpw().
Should I use bcrypt or Argon2 in 2026?
Both are good choices. Argon2 (via the argon2-cffi library) is the current OWASP recommendation as it won the Password Hashing Competition and is more resistant to GPU-based attacks.
Does each call to gensalt() produce a different hash?
Yes. Every time you call bcrypt.gensalt() a new random salt is generated, so the same password produces a different hash each time. This is intentional. The salt is embedded in the hash string, so bcrypt.checkpw() can always verify correctly.
Password hashing is a non-negotiable security practice. Implementing bcrypt in Python is straightforward and protects your users against the most common types of credential attacks.






