By FixTheVuln Team
Peer-reviewed security content
Sources: CISA, NVD, OWASP
Key Takeaways
- NIST recommends minimum 8 characters, but 14+ characters is best practice
- Length matters more than complexity โ passphrases are more secure than short complex passwords
- Require multi-factor authentication (MFA) โ it stops 99.9% of credential attacks
- Check passwords against known breach databases (Have I Been Pwned)
- Don't force periodic password changes unless there's evidence of compromise
Test Your Knowledge
Modern Password Policy Standards
Updated password guidance based on NIST SP 800-63B and industry best practices. Focus on length over complexity, eliminate outdated requirements, and implement strong authentication.
NIST SP 800-63B Password Guidelines
What NIST Recommends
| Recommendation | Details |
|---|---|
| Minimum Length | 8 characters minimum, 15+ recommended |
| Maximum Length | At least 64 characters should be allowed |
| Character Types | Allow ALL printable ASCII, Unicode, spaces |
| Passphrases | Encourage use of passphrases over complex passwords |
| Blocklist | Check against breached password databases |
| No Expiration | Don't force periodic password changes |
| No Hints | Don't allow password hints or knowledge-based questions |
What NIST Says to AVOID
- Composition rules - Don't require uppercase, lowercase, numbers, symbols
- Mandatory rotation - Don't force password changes every 30/60/90 days
- Security questions - Don't use "What's your mother's maiden name?"
- SMS-only 2FA - SMS is vulnerable to SIM swapping
- Password hints - These leak information about passwords
- Truncation - Don't silently truncate passwords
Length vs Complexity: The Math
Password Entropy Comparison
| Password Type | Example | Entropy (bits) | Time to Crack* |
|---|---|---|---|
| 8 char complex | P@ssw0rd | ~52 bits | Minutes (common) |
| 8 char random | kX9#mL2@ | ~52 bits | Hours to days |
| 12 char random | Tm5#kL9@pQ2x | ~78 bits | Years |
| 4-word passphrase | correct-horse-battery-staple | ~44 bits | Months |
| 6-word passphrase | correct-horse-battery-staple-purple-elephant | ~77 bits | Centuries |
| 16 char random | Tm5#kL9@pQ2xRv7! | ~104 bits | Heat death of universe |
*Assuming 100 billion guesses/second and offline attack
Why Length Wins
# Entropy calculation
# Entropy = log2(character_set_size ^ password_length)
# 8 characters, 95 printable ASCII
entropy = log2(95^8) = ~52.6 bits
# 16 characters, 95 printable ASCII
entropy = log2(95^16) = ~105.2 bits
# Each additional character DOUBLES the keyspace
# Adding complexity requirements barely helps
# and makes passwords harder to remember
Multi-Factor Authentication (MFA)
MFA Methods Ranked by Security
| Rank | Method | Security | Notes |
|---|---|---|---|
| 1 | Hardware Security Key (FIDO2/WebAuthn) | Excellent | Phishing-resistant, YubiKey, Google Titan |
| 2 | Passkeys | Excellent | Device-bound, phishing-resistant |
| 3 | Authenticator App (TOTP) | Good | Google Authenticator, Authy, 1Password |
| 4 | Push Notification | Good | Duo, Microsoft Authenticator |
| 5 | SMS/Voice OTP | Fair | Vulnerable to SIM swap, SS7 attacks |
| 6 | Email OTP | Fair | Only as secure as email account |
| 7 | Security Questions | Poor | Not true MFA, easily researched |
FIDO2/WebAuthn Implementation
// Register a new passkey (JavaScript)
async function registerPasskey() {
const publicKeyCredentialCreationOptions = {
challenge: new Uint8Array(32), // from server
rp: {
name: "My Application",
id: "example.com"
},
user: {
id: new Uint8Array(16), // user ID from server
name: "[email protected]",
displayName: "User Name"
},
pubKeyCredParams: [
{ alg: -7, type: "public-key" }, // ES256
{ alg: -257, type: "public-key" } // RS256
],
authenticatorSelection: {
authenticatorAttachment: "platform",
userVerification: "required",
residentKey: "required"
},
timeout: 60000,
attestation: "none"
};
const credential = await navigator.credentials.create({
publicKey: publicKeyCredentialCreationOptions
});
// Send credential to server for storage
}
Secure Password Storage
Hashing Algorithms (Best to Worst)
| Algorithm | Recommendation | Work Factor |
|---|---|---|
| Argon2id | Preferred choice | m=65536, t=3, p=4 |
| bcrypt | Excellent, widely supported | cost=12 minimum |
| scrypt | Good alternative | N=2^17, r=8, p=1 |
| PBKDF2-SHA256 | Acceptable if others unavailable | 600,000+ iterations |
| SHA-256 (salted) | NOT RECOMMENDED | Too fast for passwords |
| MD5, SHA-1 | NEVER USE | Broken, easily cracked |
Implementation Examples
# Python - Using Argon2
from argon2 import PasswordHasher
ph = PasswordHasher(
time_cost=3,
memory_cost=65536,
parallelism=4,
hash_len=32,
salt_len=16
)
# Hash password
hash = ph.hash("user_password")
# Verify password
try:
ph.verify(hash, "user_password")
if ph.check_needs_rehash(hash):
# Update hash with new parameters
new_hash = ph.hash("user_password")
except VerifyMismatchError:
print("Invalid password")
# Node.js - Using bcrypt
const bcrypt = require('bcrypt');
const saltRounds = 12;
// Hash password
const hash = await bcrypt.hash(password, saltRounds);
// Verify password
const match = await bcrypt.compare(password, hash);
# PHP - Using password_hash (bcrypt by default)
// Hash
$hash = password_hash($password, PASSWORD_ARGON2ID, [
'memory_cost' => 65536,
'time_cost' => 4,
'threads' => 3
]);
// Verify
if (password_verify($password, $hash)) {
if (password_needs_rehash($hash, PASSWORD_ARGON2ID)) {
// Rehash with updated parameters
}
}
Policy Implementation Checklist
Registration/Password Change
- Minimum 12 characters (8 absolute minimum)
- Maximum at least 64 characters
- Allow all Unicode characters including spaces
- Check against breached password list (HaveIBeenPwned API)
- Show real-time password strength meter
- Don't allow username/email in password
- Encourage passphrases with clear examples
Breached Password Check
# Check password against HaveIBeenPwned (k-anonymity)
import hashlib
import requests
def check_pwned(password):
sha1 = hashlib.sha1(password.encode()).hexdigest().upper()
prefix, suffix = sha1[:5], sha1[5:]
response = requests.get(
f'https://api.pwnedpasswords.com/range/{prefix}',
headers={'Add-Padding': 'true'}
)
for line in response.text.splitlines():
hash_suffix, count = line.split(':')
if hash_suffix == suffix:
return int(count) # Found in breaches
return 0 # Not found
# Usage
breach_count = check_pwned("password123")
if breach_count > 0:
print(f"Password found in {breach_count} breaches!")
Account Security
- Require MFA for all accounts (hardware key preferred)
- Rate limit login attempts (5 attempts per 15 minutes)
- Implement account lockout with exponential backoff
- Send notifications on password change/new device login
- Provide recovery codes for MFA backup
- Log all authentication events
- Implement secure password reset flow
Password Policy Quick Reference
| Setting | Recommended Value | Rationale |
|---|---|---|
| Minimum length | 12-15 characters | Entropy over complexity |
| Maximum length | 64-128 characters | Support passphrases |
| Complexity rules | None required | NIST recommends against |
| Expiration | None (or 1 year max) | Only on breach detection |
| History | Last 10-24 passwords | Prevent reuse |
| Lockout threshold | 5-10 attempts | Prevent brute force |
| Lockout duration | 15-30 min or exponential | Slow down attacks |
| MFA requirement | Required for all | Defense in depth |
| Hashing algorithm | Argon2id or bcrypt | Memory-hard, slow |
| Breach checking | On every change | Prevent known passwords |
Related Resources
Need Detailed Password Security Guides?
For comprehensive tutorials and implementation guides:
Visit FixTheVuln.com →FixTheVuln Store
Get the CompTIA Security+ Study Planner
Fillable PDF study planners with domain trackers, weekly schedules, and progress tracking. Available in Standard, ADHD-Friendly, Dark Mode, and 4-Format Bundle.
CompTIA Security+ Planner60+ certifications available — from $5.99
CyberFolio
Choosing your next cert? Track them all in one place.
Build a shareable cybersecurity portfolio that highlights your certifications, projects, and skills — free.
Build Your Portfolio →