The Developer's Guide to Password Security

BY TOOLS.FUN  ·  MARCH 28, 2026  ·  6 min read

Password security vulnerabilities cause some of the most damaging data breaches in the industry. The patterns that lead to them — MD5 password hashing, unsalted hashes, storing plaintext passwords, weak secrets management — are surprisingly common even in professional codebases. This guide gives you the foundational knowledge to handle credentials correctly from day one.

Related tools: use our AES Encryptor for encrypting stored secrets, Base64 Encoder to encode credentials in HTTP headers, Hex Converter to inspect hash outputs, and RegExp Tester to build password validation patterns.

Never Store Passwords as MD5

The most common password security mistake is hashing passwords with MD5 (or SHA-1, or SHA-256). The problem isn't that these algorithms are wrong in general — SHA-256 is excellent for file integrity checks. The problem is that they're fast. A modern GPU can compute billions of MD5 hashes per second.

If an attacker steals your password database and finds an MD5 hash like 5f4dcc3b5aa765d61d8327deb882cf99, they can crack it in under a second using a rainbow table lookup (that's "password", by the way). Even without rainbow tables, brute-forcing an unsalted MD5 hash of a common password takes milliseconds.

# WRONG — never do this
import hashlib
stored_hash = hashlib.md5(password.encode()).hexdigest()

# ALSO WRONG — SHA-256 is still too fast for passwords
stored_hash = hashlib.sha256(password.encode()).hexdigest()
Key point: Fast hash functions (MD5, SHA-1, SHA-256) are wrong for passwords not because they're broken, but because they're too fast. Password hashing must be deliberately slow to resist brute-force attacks.

Proper Password Hashing with bcrypt

bcrypt was designed in 1999 specifically for password hashing. It has a configurable work factor (cost) that lets you tune how slow it is — slow enough to deter brute force, fast enough for legitimate login. It automatically generates and stores a salt, preventing rainbow table attacks.

# Python — using bcrypt
import bcrypt

# Hashing (at registration)
password = b"user_password"
hashed = bcrypt.hashpw(password, bcrypt.gensalt(rounds=12))
# Store 'hashed' in the database

# Verification (at login)
if bcrypt.checkpw(password, hashed):
    print("Password matches")

# Node.js — using bcryptjs
const bcrypt = require('bcryptjs');
const hash = await bcrypt.hash(password, 12);
const match = await bcrypt.compare(password, hash);

Argon2id is the recommended algorithm for new systems — it won the 2015 Password Hashing Competition and is memory-hard (resists GPU and ASIC attacks far better than bcrypt). Use the argon2-cffi library in Python or argon2id in Node.js.

# Python — using Argon2id
from argon2 import PasswordHasher
ph = PasswordHasher(time_cost=2, memory_cost=65536, parallelism=2)
hash = ph.hash("user_password")
ph.verify(hash, "user_password")  # raises VerifyMismatchError if wrong
Key point: The work factor (cost/rounds) should be set so hashing takes 100–300ms on your production hardware. This is imperceptible to users but makes offline brute-force attacks prohibitively expensive. Increase the cost as hardware gets faster.

What is Password Entropy?

Entropy measures how unpredictable a password is, expressed in bits. A password with N bits of entropy would take on average 2N-1 guesses to crack by brute force. Entropy depends on both the length of the password and the size of the character set used:

A random 12-character password using full ASCII has ~79 bits of entropy — sufficient for most purposes. A passphrase of 5 random common words has ~65 bits of entropy but is far more memorable. The key word is random — human-chosen passwords have far less entropy than the mathematics suggests because humans are predictable.

Generate cryptographically random passwords with the Password Generator at Tools.Fun, which uses your browser's crypto.getRandomValues() API for true randomness.

Password Policy Best Practices

Counterintuitively, many traditional password policies (mandatory complexity rules, forced periodic rotation) actively harm security. NIST's current guidelines (SP 800-63B) recommend:

Key point: The best password policy encourages long, random, unique passwords stored in a password manager — not complexity rules that lead to P@ssw0rd1.

Secrets Management for Applications

Application secrets (database passwords, API keys, encryption keys) should never be hardcoded in source code. The minimal acceptable approach is environment variables, which keep secrets out of version control. Better approaches for production:

# Load secrets from environment (Python)
import os
db_password = os.environ['DB_PASSWORD']  # Raises KeyError if not set

# Safer — fails loudly with a clear message
db_password = os.environ.get('DB_PASSWORD')
if not db_password:
    raise RuntimeError("DB_PASSWORD environment variable is required")

API Key Generation

API keys should be generated using a cryptographically secure random number generator. They should be long enough (at least 128 bits / 16 bytes of entropy) to be unguessable, and should be stored hashed in your database (just like passwords) so that a database breach doesn't expose all your API keys.

# Python — generate a secure API key
import secrets
api_key = secrets.token_urlsafe(32)  # 256 bits of entropy, URL-safe

# Store only the hash
import hashlib
stored_hash = hashlib.sha256(api_key.encode()).hexdigest()

# At verification time: hash the presented key and compare
Key point: Show the API key to the user exactly once (at generation) and store only its hash. This way, a breach of your database doesn't expose live API keys. It also makes key revocation straightforward.

Service Account Credentials

Service accounts (non-human accounts used by applications and automated systems) are often the weakest link. Common mistakes: shared credentials between environments, credentials committed to Git, overly broad permissions, credentials that never expire. Use short-lived tokens where possible (OAuth 2.0 client credentials with short-lived JWTs), rotate long-lived credentials regularly, and audit access logs for anomalous service account activity.

Generate Secure Passwords

The Password Generator at Tools.Fun creates cryptographically random passwords with configurable length and character sets. The Hash Generator lets you verify MD5 and SHA hashes for testing and learning purposes — but remember, never use MD5 or SHA for production password storage.

← Back