Cybersecurity and Privacy Essentials
Write safer code by understanding common threats and defensive techniques.
Content
Passwords, Hashing, and Salting
Versions:
Watch & Learn
AI-discovered learning video
Sign in to watch the learning video for this topic.
Passwords, Hashing, and Salting — Practical Secure Storage for Flask Apps
Imagine your app is a castle. Passwords are keys. You would not store keys in a sticky note taped to the drawbridge, right? Welcome to the part where we stop doing that.
Why this matters (building on Threat Modeling and OWASP)
You already learned Threat Modeling and the OWASP Top 10. One of the recurring villains there is Broken Authentication — account takeover, credential stuffing, leaked databases. Threat modeling tells you attackers want access, so the most important defense is how you store and check credentials. If your Flask server and database (from Servers and Flask Web Applications) are the place where users log in, this is exactly where hashing and salting earn their keep.
What the terms mean, quickly
- Password: what a user types to prove identity.
- Hashing: running the password through a one-way function to produce a fixed-size string (a digest). You cannot feasibly reverse it.
- Salt: a random value mixed into the password before hashing so that identical passwords do not produce identical hashes.
- Pepper: a server-side secret added to every password before hashing (stored separately from the database).
Micro explanation
Hashing is like turning a key into a fingerprint. Salting is like stamping each key with a unique sticker before fingerprinting it — two identical keys no longer look identical in your records.
Bad idea vs good idea (the TL;DR)
Bad: store raw passwords in the users table. Also bad: store unsalted MD5/SHA1 of passwords. Attackers love rainbow tables and fast hashes.
Good: use a slow, adaptive password hash function like bcrypt, Argon2, or PBKDF2 with a unique salt per password, and an appropriate cost factor. Use a pepper if you want defense-in-depth.
Why slow hashing matters
Attackers run billions of guesses per second on GPUs and ASICs. Fast hashes (MD5, SHA256) let them try far more guesses. Slow adaptative hashes purposely take time and memory, making cracking expensive.
- bcrypt: CPU-bound, cost factor (rounds) increases time.
- Argon2: winner of the password hashing competition; tunable memory and time cost — great for modern security.
- PBKDF2: widely supported, can be used safely with many iterations.
How to do this in a Flask app (practical example)
Assume a simple users table with columns: id, email, password_hash.
Python example using werkzeug security (convenient) and bcrypt via flask-bcrypt. These examples avoid double quotes so you can paste directly.
Code: storing a new user
from werkzeug.security import generate_password_hash, check_password_hash
# When creating account
plain = 'user password input'
hash = generate_password_hash(plain, method='pbkdf2:sha256', salt_length=16)
# store hash in users.password_hash
# For verification
is_ok = check_password_hash(hash, 'user entered password')
Code: using bcrypt (recommended) with flask-bcrypt
from flask_bcrypt import Bcrypt
bcrypt = Bcrypt(app)
pw = 'user password input'
hash = bcrypt.generate_password_hash(pw) # includes salt automatically
# store hash.decode('utf-8')
# Check
bcrypt.check_password_hash(stored_hash, 'login attempt')
Notes:
- Both methods include a unique salt per hash. You do not need to store the salt separately when using these libraries; the salt is embedded in the hash string.
- For Argon2, use argon2-cffi. API is similar and supports memory and time tuning.
Salts, peppers, and storage details
- Use a unique salt per user/password. That prevents attackers from exploiting duplicate passwords across accounts.
- Store the salt in the database alongside the hash if your library does not embed it. Most modern libs embed it.
- Pepper is optional: store it outside the database (in environment vars or secret manager). If the DB leaks, the attacker still needs the pepper. But beware: if pepper is in app code on the same host an attacker can reach, it is less effective.
Migration and upgrades
When you improve your hashing algorithm or cost factor:
- Store the new hashes using the new algorithm on next login or password change.
- For existing users, mark old hashes by algorithm/version and rehash on successful login.
- Optionally force password resets for highly sensitive systems.
Common pitfalls and attacks to watch
- Reusing fast hashes like MD5/SHA1: cheap to crack.
- Using the same salt globally: defeats the purpose; unique per-password is required.
- Storing pepper in the same DB: one compromise = everything exposed.
- Timing attacks on password comparison: use constant-time comparisons (check_password_hash handles this).
- Poor password policies: weak user passwords still get cracked.
Integrating with your threat model
From Threat Modeling: identify the attacker and the asset (user accounts). For credential theft, prioritize the following mitigations:
- Use strong hashing + salts + pepper if possible.
- Improve account protections: MFA, rate limiting, IP or behavior-based locking.
- Monitor for leaked hashes and rotate credentials if needed.
This fits naturally into the OWASP Top 10 item about Broken Authentication: correct password storage is the most important backend control to prevent large-scale account compromise.
Quick checklist for secure password storage
- Use bcrypt, Argon2, or PBKDF2 with a high cost.
- Use a unique salt per password.
- Keep salts and hashes in the user table; pepper in a separate secret store.
- Verify with constant-time checks.
- Rehash on login when cost or algorithm changes.
- Add MFA and rate limiting for defense-in-depth.
Key takeaways
- Never store plain passwords. Ever.
- Prefer slow, adaptive hashing algorithms with unique salts.
- Use the right libraries in Flask to avoid mistakes; they handle salt details for you.
- Combine hashed storage with multi-factor protections and monitoring as part of your threat model.
This is the moment where the concept finally clicks: hashes protect passwords from being immediately useful to attackers, salts prevent bulk compromise, and slow hashing makes cracking prohibitively expensive.
Need a tiny homework nugget? In your Flask app from Servers and Flask Web Applications, implement user registration and login using bcrypt or Argon2, log failed attempts, and add a simple rate limiter. You will see how these pieces from Threat Modeling and OWASP fit together like a security sandwich.
Comments (0)
Please sign in to leave a comment.
No comments yet. Be the first to comment!