X Xerobit

Password Policy Guide — NIST 2024 Guidelines and Modern Best Practices

NIST SP 800-63B 2024 updated password guidelines: no mandatory rotation, no complexity rules, require minimum 15 characters, check against breached passwords. Learn modern...

Mian Ali Khalid · · 5 min read
Use the tool
Password Generator
Generate strong random passwords with configurable length, character classes, and exclusions. Real entropy meter, crack-time estimate, bulk mode.
Open Password Generator →

NIST’s 2024 update to SP 800-63B reversed many traditional password policies. Forced rotation, complexity rules with special characters, and security questions are now explicitly discouraged. Here’s what modern password policy looks like.

Use the Password Generator to generate compliant passwords.

NIST SP 800-63B 2024 key changes

Dropped:

  • Mandatory periodic password rotation (was: every 60-90 days)
  • Complexity requirements like “must contain uppercase, digit, and symbol”
  • Security questions as authentication factor
  • SMS-based OTP as “sufficient” second factor (now “RESTRICTED”)

Added/strengthened:

  • Minimum 15 characters for organizational accounts
  • Check against breached password lists (Have I Been Pwned)
  • Allow all printable ASCII + spaces
  • Support paste into password fields
  • No truncation of passwords

Minimum length

// Old convention: 8 characters
// NIST 2024: minimum 8 user-chosen, prefer 15+, allow up to 64+

function validatePasswordLength(password) {
  if (password.length < 8) {
    return { valid: false, message: 'Password must be at least 8 characters' };
  }
  if (password.length > 256) {
    return { valid: false, message: 'Password must be 256 characters or fewer' };
  }
  return { valid: true };
}

// For higher-security contexts (enterprise, admin accounts):
const MIN_LENGTH = 15;

Check against breached passwords (HIBP)

Have I Been Pwned API uses k-Anonymity — you only send the first 5 chars of the SHA-1 hash:

import crypto from 'crypto';

async function isBreachedPassword(password) {
  const hash = crypto.createHash('sha1').update(password).digest('hex').toUpperCase();
  const prefix = hash.slice(0, 5);
  const suffix = hash.slice(5);
  
  const response = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`, {
    headers: { 'Add-Padding': 'true' },
  });
  
  const text = await response.text();
  const lines = text.split('\r\n');
  
  for (const line of lines) {
    const [hashSuffix, count] = line.split(':');
    if (hashSuffix === suffix) {
      return { breached: true, count: parseInt(count) };
    }
  }
  
  return { breached: false, count: 0 };
}

// Usage:
const result = await isBreachedPassword('password123');
if (result.breached) {
  console.log(`This password appeared ${result.count.toLocaleString()} times in data breaches`);
}

No complexity rules (but encourage length)

// OLD (bad) approach:
function hasComplexity(pw) {
  return /[A-Z]/.test(pw) && /[a-z]/.test(pw) && /[0-9]/.test(pw) && /[^a-zA-Z0-9]/.test(pw);
}
// Forces users to make predictable substitutions: P@ssw0rd!

// NIST 2024: don't enforce complexity, but you MAY reject specific patterns:
function isCommonPattern(pw) {
  const lower = pw.toLowerCase();
  const common = ['password', '123456', 'qwerty', 'abc123', 'letmein', 'admin', 'welcome'];
  return common.some(c => lower.includes(c));
}

// Instead, calculate and show strength:
function passwordStrength(pw) {
  const patterns = {
    hasUpper: /[A-Z]/.test(pw),
    hasLower: /[a-z]/.test(pw),
    hasDigit: /[0-9]/.test(pw),
    hasSymbol: /[^a-zA-Z0-9]/.test(pw),
  };
  const charsetSize = 
    (patterns.hasLower ? 26 : 0) +
    (patterns.hasUpper ? 26 : 0) +
    (patterns.hasDigit ? 10 : 0) +
    (patterns.hasSymbol ? 32 : 0);
  
  const entropy = pw.length * Math.log2(Math.max(charsetSize, 1));
  
  if (entropy < 40) return 'weak';
  if (entropy < 60) return 'fair';
  if (entropy < 80) return 'strong';
  return 'very strong';
}

Allow paste and all characters

<!-- Don't disable paste! Users with password managers need it: -->
<input type="password" id="password" autocomplete="current-password">
<!-- Remove any onpaste="return false" attributes -->

<!-- Allow all printable ASCII including spaces: -->
<!-- No maxlength that's too short (use 256 as maximum) -->
<input type="password" id="password" maxlength="256">

Zxcvbn — realistic strength estimation

// npm install zxcvbn
import zxcvbn from 'zxcvbn';

const result = zxcvbn('correct horse battery staple');
// result.score: 0-4 (0=weak, 4=very strong)
// result.guesses: estimated number of guesses
// result.crack_times_display.online_throttling_100_per_hour: '23 centuries'
// result.feedback.suggestions: ['Add more words']

// Passphrase: 4 common words is stronger than a complex 8-char password
zxcvbn('password123!')  // score: 2 (weak despite complexity)
zxcvbn('correct horse battery staple')  // score: 4 (strong despite no symbols)

Related posts

Related tool

Password Generator

Generate strong random passwords with configurable length, character classes, and exclusions. Real entropy meter, crack-time estimate, bulk mode.

Written by Mian Ali Khalid. Part of the Dev Productivity pillar.