Password Hashing with Werkzeug Security0%

Password Hashing with Werkzeug Security

Beginner12 min read•Updated: Jul 11, 2026
Study MaterialsCheatsheet

Password Hashing with Werkzeug Security

Storing plaintext passwords in a database is one of the most catastrophic security failures a web developer can make. If a database is breached or leaked, plaintext passwords immediately compromise user accounts across every service where they reuse that password.

Werkzeug provides cryptographic password hashing via its werkzeug.security module, utilizing salted PBKDF2 or scrypt algorithms.


1. How Cryptographic Hashing Works

A cryptographic hash function is a one-way function:

  • Given an input password, it produces a fixed-length hexadecimal digest.
  • It is mathematically infeasible to reverse-engineer the original password from the digest.
Visual Architecture Blueprint
+-------------------+        Cryptographic Algorithm         +-------------------------------------+
| Plaintext Password| =====================================> | Salted Hash Digest                  |
| "SecretP@ss2026!" |   (PBKDF2-HMAC-SHA256 + 16-byte salt)  | "pbkdf2:sha256:600000$8xG...$9mP..." |
+-------------------+                                        +-------------------------------------+

The Role of Salt:

A salt is a cryptographically random byte string generated for every individual password before hashing. Salts prevent rainbow table attacks (precomputed hash lookup tables) and ensure that two users with identical passwords will have completely different hashes!


2. Generating Hashes with generate_password_hash

Python
from werkzeug.security import generate_password_hash, check_password_hash
 
# Hash a plaintext password
plaintext = "SuperSecretPassword123"
hashed_password = generate_password_hash(
plaintext,
method="pbkdf2:sha256:600000", # Algorithm and iteration work factor
salt_length=16
)
 
print(hashed_password)
# Output format: method$salt$hash
# Example: 'pbkdf2:sha256:600000$hK4J8vL2...$8f4a7c1b...'

3. Verifying Passwords with check_password_hash

When a user attempts to log in, never attempt to decrypt the stored hash. Instead, pass the entered password and the stored hash into check_password_hash():

Python
entered_password = "SuperSecretPassword123"
 
# Returns True if password matches; False otherwise
is_valid = check_password_hash(hashed_password, entered_password)
 
if is_valid:
print("Authentication successful!")
else:
print("Invalid credentials.")

check_password_hash extracts the salt and algorithm parameters from the stored hash string, hashes the entered password using those exact parameters, and uses a constant-time comparison algorithm to prevent timing attacks.


4. Integrating Password Hashing into a User Model

Encapsulate password handling directly on your SQLAlchemy User model using Python properties:

Python
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
 
db = SQLAlchemy()
 
class User(db.Model):
__tablename__ = "users"
 
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
# Store only the hashed string, NEVER the plaintext password!
password_hash = db.Column(db.String(256), nullable=False)
 
@property
def password(self):
# Prevent reading password attribute directly
raise AttributeError("password is not a readable attribute")
 
@password.setter
def password(self, plaintext_password):
# Automatically hashes password when setting user.password = "secret"
self.password_hash = generate_password_hash(plaintext_password)
 
def verify_password(self, plaintext_password):
# Instance method to verify login credentials
return check_password_hash(self.password_hash, plaintext_password)

Usage in Registration & Login:

Python
# Registration
new_user = User(username="sumit", email="sumit@example.com")
new_user.password = "StrongPass2026!" # Automatically hashed!
db.session.add(new_user)
db.session.commit()
 
# Login Verification
user = User.query.filter_by(username="sumit").first()
if user and user.verify_password("StrongPass2026!"):
# Log user in
pass

Interactive Quiz

Practice Quiz

Question 1
Which two functions from werkzeug.security manage password security in Flask?
Question 2
Why is a random "salt" added to passwords before hashing?
Question 3
Why does attempting to read user.password raise an AttributeError in the recommended User model pattern?
Question 4
Why does check_password_hash use a constant-time comparison algorithm?
Question 5
Can a properly generated cryptographic hash be decrypted back into the original plaintext password?
Next Lesson

User Session Management with Flask-Login

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext