NIST Password Guidelines: The Rules to Use in Your App
NIST's guidance dropped the rules most sign-up forms still enforce. Here is what it recommends instead, why length beats symbols in numbers, and how to generate passwords securely in code.
The NIST password guidelines, in Special Publication 800-63B, are the reference most security teams follow, and they contradict many sign-up forms still in use: no forced symbols, no expiry every 90 days. The Password Generator creates strong passwords in the browser, and its page explains why length matters more than character variety. This guide is for the other side: the rules to build into an application that accepts passwords.
What the NIST password guidelines require
- Length first. The 2025 revision requires at least 15 characters when a password is the only factor, and at least 8 when it is combined with a second factor such as an authenticator app.
- Allow long passwords. Accept at least 64 characters, so passphrases and password managers work.
- Accept any characters. Spaces and Unicode included. Do not silently cut or strip what the user typed.
- No composition rules. Do not demand an uppercase letter, a digit and a symbol. Such rules push people towards predictable patterns like
Summer2026!. - No scheduled expiry. Require a change only when there is evidence the password was exposed.
- Check against known bad passwords. Reject new passwords that appear in lists of breached, common or obvious choices, such as the service name or
password1. - No hints and no security questions. Both are easier to guess than the password.
Password length vs complexity in numbers
For randomly generated passwords, strength is measured in bits of entropy: length multiplied by the bits each character carries. Worked out:
8 characters from all 94 printable symbols 52.4 bits
15 lowercase letters 70.5 bits
12 characters from all 94 printable symbols 78.7 bits
4 random words from a 7,776-word list 51.7 bits
6 random words from a 7,776-word list 77.5 bitsFifteen lowercase letters beat eight characters with every symbol class by 18 bits, which means about 275,000 times more guesses. That is the reasoning behind length over complexity. These figures only hold for randomly generated passwords; a human-chosen Summer2026! has far less entropy than its length suggests, which is why the breached-password check matters.
Storing passwords
Never store passwords themselves, encrypted or not. Store a hash from a password hashing function designed to be slow, such as Argon2id, scrypt or bcrypt, with a unique salt per password. General-purpose hashes like SHA-256 are far too fast for this: they are built for checking files, and an attacker with a stolen database can try billions of guesses per second against them.
Generate a secure password in code
import secrets, string
alphabet = string.ascii_letters + string.digits + string.punctuation
password = "".join(secrets.choice(alphabet) for _ in range(16))const alphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
function generatePassword(length = 16) {
const limit = 256 - (256 % alphabet.length);
let password = "";
while (password.length < length) {
const [byte] = crypto.getRandomValues(new Uint8Array(1));
if (byte < limit) password += alphabet[byte % alphabet.length];
}
return password;
}Use secrets in Python and crypto.getRandomValues in JavaScript, never random or Math.random, which are not designed to be unpredictable. The JavaScript version discards random bytes at or above the largest multiple of the alphabet size, so every character is equally likely; a plain byte % 94 would make the first characters of the alphabet slightly more common.
Related tools
Related guides
A published SHA-256 checksum only helps if you compare it. Here are the exact commands for each operating system, a one-step check against a checksum file, and code for Node.js and Python.
Every modern runtime can generate a UUID without a library. Here are the one-liners for JavaScript, Python and PostgreSQL, a tested UUID v7 function, and two common mistakes.
Every new table and every public URL needs an ID scheme. Here is how Nano ID, UUID and auto-increment compare, with the collision maths worked out and a tested Nano ID function.