DevToys Web Pro iconDevToys Web Pro블로그
번역: LocalePack logoLocalePack
평가하기:
브라우저 확장 프로그램을 사용해 보세요:
← Back to Blog

Password Generator Guide: Entropy, Character Sets, and NIST 2024

12 min read

Most passwords are not random. They follow patterns — capitalized first letter, number at the end, common word substitutions — that attackers have catalogued and baked into their cracking dictionaries. A good password generator sidesteps all of that by drawing from a cryptographically secure source of randomness and giving you direct control over the alphabet it samples from. Use the Password Generator to follow along as you read.

What "Strong" Actually Means

Password strength is measured in entropy bits. Entropy quantifies how many equally likely possibilities an attacker must search through when brute-forcing. The formula is straightforward:

entropy = length × log₂(charset_size)

If your charset has 62 characters (a–z, A–Z, 0–9) and your password is 16 characters long:

entropy = 16 × log₂(62) = 16 × 5.95 95 bits

Each additional bit doubles the search space. Going from 64 bits to 96 bits does not add 50% more work for the attacker — it multiplies the work by 2^32, which is about 4 billion times harder.

EntropyResistanceSuitable For
< 40 bitsCracked in seconds on modern hardwareNothing
40–60 bitsHours to days on a GPU clusterLow-value, short-lived tokens only
60–80 bitsYears without a breach + fast hashAcceptable minimum for online accounts
80–128 bitsDecades to centuries on any plausible hardwarePassword manager entries, API keys
> 128 bitsComputationally infeasible for the foreseeable futureEncryption keys, master passwords

These estimates assume the attacker has obtained a hashed copy of your password and is running an offline attack. Against online login forms with rate limiting, even 40 bits is practically uncrackable — but breached databases are the real threat model.

Length vs. Complexity

A common misconception is that requiring symbols makes passwords significantly stronger. Compare these two passwords, both at a similar entropy level:

PasswordCharset SizeLengthEntropyCrack Time (10B/s)
correct-horse-battery26 (lowercase + hyphen)21 chars~99 bits> 10,000 years
xK9#mQ2!95 (full printable ASCII)8 chars~52 bits~1 hour
Tr0ub4dor&39511 chars~71 bits~550 years
n7Xq2pLm9Rv4kYwZ62 (alphanumeric)16 chars~95 bits> 1,000 years

The 8-character symbol-heavy password is far weaker than a 16-character alphanumeric one, yet it looks more "complex." Length is the dominant factor. Adding symbols to a short password is a worse tradeoff than simply making the password longer.

The crack-time figures above assume 10 billion guesses per second — achievable with a consumer GPU against a fast hash like MD5 or SHA-1. Against a proper slow hash (bcrypt, Argon2), those times stretch by 5–6 orders of magnitude. See the hashing guide for details, and use the bcrypt generator to hash passwords for storage.

Character Sets

Each character set adds a different amount of entropy per character. Here is the math for common sets:

Character SetSizeBits/Char16-char entropy
Lowercase only (a–z)264.7075 bits
Lowercase + uppercase525.7091 bits
Alphanumeric (a–z, A–Z, 0–9)625.9595 bits
Alphanumeric + 10 symbols726.1799 bits
Full printable ASCII956.57105 bits
Hex (0–9, a–f)164.0064 bits

Going from alphanumeric to full ASCII saves you roughly one character of length for the same entropy — a minor gain that often is not worth the tradeoff of having passwords that break some forms, terminals, or config file parsers that mishandle quotes and backslashes.

Ambiguous characters

Some generators offer an "exclude ambiguous characters" option that removes 0, O, l, 1, and I. This reduces the charset from 62 to 57 for alphanumeric, costing about 0.13 bits per character — negligible. Enable it when passwords need to be read aloud or typed manually. Disable it for passwords that go directly into a password manager and are never typed by hand.

NIST 2024 Guidelines

NIST SP 800-63B (updated in 2024) is the authoritative US standard for password policy. Its main changes from older guidance are worth knowing:

  • Minimum length is 8 characters for user-chosen passwords, but NIST recommends supporting up to at least 64 characters. Services that cap passwords at 12 or 16 characters are noncompliant and harm security.
  • No forced composition rules. Requiring uppercase, numbers, and symbols does not meaningfully increase security and leads to predictable patterns (Password1!). NIST explicitly advises against mandating specific character types.
  • No periodic rotation unless there is evidence of compromise. Forced rotation causes users to make incremental, predictable changes (Password1! Password2!).
  • Check against breach lists. Verifiers should reject passwords found in known data breaches. Services like Have I Been Pwned expose a k-anonymity API that lets you check without transmitting the full password.
  • Allow all printable Unicode including spaces. Blocking characters like & or " pushes users toward weaker passwords.

The practical takeaway for password generation: maximize length, use a broad but safe charset, and let the output go straight into a password manager rather than forcing users to memorize something they will simplify.

Passphrases and Diceware

A passphrase is a sequence of randomly chosen words rather than random characters. The classic method is Diceware: roll five physical dice, look up the resulting number in the EFF large wordlist (7,776 words), and repeat for each word. The entropy per word is:

log₂(7776) ≈ 12.92 bits per word
Word CountEntropyExampleCrack Time (10B/s)
4 words~51 bitspiano-surge-clinic-waffle~1 hour
5 words~64 bitspiano-surge-clinic-waffle-orbit~580 years
6 words~77 bitspiano-surge-clinic-waffle-orbit-fudge~1.2 billion years
7 words~90 bitspiano-surge-clinic-waffle-orbit-fudge-toniccomputationally infeasible

Passphrases have one advantage over random character strings: humans can memorize them. A 6-word Diceware passphrase at 77 bits is stronger than the typical 8-character symbol-bearing password and can be typed from memory. The downside is length — 35–45 characters — which breaks some legacy systems with short password fields.

Important: the word selection must be uniformly random. Choosing words yourself or from a biased source (common words you like) eliminates most of the entropy. Proper Diceware uses physical dice or a CSPRNG. A 4-word passphrase where you pick "comfortable" words has roughly the entropy of 2–3 random words.

Where Passwords Go After Generation

A generated password is only as safe as where you store it. The correct destination is a password manager — never:

  • A plaintext file or notes app
  • A spreadsheet, even encrypted with a weak password
  • A browser bookmark title or URL field
  • Your email drafts folder
  • A sticky note, physical or digital

Password managers (Bitwarden, 1Password, KeePassXC) encrypt your vault with a master password and, critically, use a slow key derivation function (PBKDF2, Argon2) to protect the vault encryption key even if the file is stolen.

On the server side, passwords must never be stored in plaintext or with a fast hash (MD5, SHA-256). Use a purpose-built password hashing algorithm:

AlgorithmMemory HardNIST / OWASP StatusNotes
Argon2idYesRecommended (OWASP first choice)Winner of PHC 2015; best modern choice
bcryptNoAcceptable64-byte input limit; work factor via cost
scryptYesAcceptableMemory-hard; harder to tune safely
PBKDF2NoAcceptable (FIPS-approved)Use with SHA-256; high iteration count required

Use the bcrypt generator to hash a password and verify a hash directly in your browser, or read the hashes and password hashing guide for a deeper look at how these algorithms work.

Programmatic Generation

When generating passwords in code, you must use a cryptographically secure pseudorandom number generator (CSPRNG). The standard library CSPRNG in every major runtime is the correct tool. Never use Math.random() or equivalent non-cryptographic generators.

Node.js — crypto.randomBytes

import { randomBytes, randomInt } from 'crypto';

const CHARSET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';

function generatePassword(length: number): string {
  // randomInt(min, max) is uniformly distributed and cryptographically secure
  return Array.from({ length }, () => CHARSET[randomInt(CHARSET.length)]).join('');
}

console.log(generatePassword(20)); // e.g. "nX4pKqL8rVwM2cYtBs9J"

// For raw random bytes (hex token, API key):
const token = randomBytes(32).toString('hex'); // 64 hex chars = 256 bits

Python — secrets module

import secrets
import string

CHARSET = string.ascii_letters + string.digits  # 62 chars

def generate_password(length: int = 20) -> str:
    # secrets.choice uses os.urandom internally — CSPRNG
    return ''.join(secrets.choice(CHARSET) for _ in range(length))

print(generate_password(20))

# URL-safe token (base64-encoded, suitable for URLs and headers):
token = secrets.token_urlsafe(32)  # 43 chars, ~192 bits of randomness

# Hex token (suitable for DB storage):
token_hex = secrets.token_hex(32)  # 64 hex chars, 256 bits

Browser — crypto.getRandomValues

function generatePassword(length: number, charset: string): string {
  const values = new Uint32Array(length);
  crypto.getRandomValues(values); // Web Crypto API — CSPRNG in all modern browsers

  // Rejection sampling: discard values that would cause modulo bias
  const max = Math.floor(0x100000000 / charset.length) * charset.length;

  return Array.from(values)
    .filter(v => v < max)               // eliminate bias
    .slice(0, length)                   // take exactly 'length' chars
    .map(v => charset[v % charset.length])
    .join('');
}

const CHARSET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
console.log(generatePassword(20, CHARSET));

The rejection sampling step above is important. Naive modulo operations on random integers introduce a small but measurable bias toward lower-index characters when the charset size does not evenly divide the integer range. For a 62-character charset and a 32-bit integer, the bias is about 0.0015% per character — tiny but non-zero.

Common Generator Pitfalls

Math.random() and non-CSPRNG sources

Math.random() in JavaScript, rand() in C, and random.random() in Python are all seeded from system entropy at startup but are not designed to be cryptographically unpredictable. An attacker who observes a few outputs can reconstruct the internal state and predict future outputs. Any password, token, or secret generated with these functions should be considered compromised.

Insufficient charset after filtering

Some generators strip "confusing" characters, special characters, and characters that break specific systems, leaving an effective charset of 30–40 characters. At 16 characters with a 36-character charset (alphanumeric, lowercase), entropy drops to 83 bits — still fine. But if you then also restrict length to 12 characters, you are at 62 bits — marginally acceptable and at risk if the hash is weak.

Predictable patterns from poor composition logic

Generators that ensure "at least one uppercase, one digit, one symbol" by placing those required characters at fixed positions (e.g., always a digit at position 0, always a symbol at the end) reduce the search space significantly. If an attacker knows the generator always produces the pattern [digit][lower×n][symbol], they search fewer combinations. Composition requirements should be enforced by generating random passwords and discarding any that do not meet the criteria — not by filling specific slots with specific character types.

Reseeding on page reload

Client-side generators that use Date.now() or the page load timestamp as a seed are vulnerable if an attacker knows roughly when a password was generated. Even a week-wide window of uncertainty has only ~600 million milliseconds — a trivial search space.crypto.getRandomValues() does not have this problem.

Quick Reference

Use CaseRecommended ConfigMin Entropy
Online account (password manager entry)20 chars, alphanumeric + symbols128 bits
Password manager master password6-word Diceware passphrase77 bits (memorable)
API key / secret tokencrypto.randomBytes(32).toString('hex')256 bits
Session tokencrypto.randomBytes(16).toString('base64url')128 bits
Temporary invite code (human-readable)12 chars, alphanumeric, no ambiguous71 bits
WiFi / device PIN (must be typed)4-word passphrase or 12 alphanumeric chars51–71 bits

Generate cryptographically secure passwords for all these scenarios directly in your browser with the Password Generator — all randomness stays on your machine, nothing is transmitted.