FixTheVuln

Password Policy Guide

By FixTheVuln Team Peer-reviewed security content Sources: CISA, NVD, OWASP

Key Takeaways

Test Your Knowledge

Security+ Practice Quiz CISSP Practice Quiz
← Back to Home

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

Password Generator Tool Password Strength Checker Encryption Cheatsheet

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+ Planner

60+ 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 →