Python Password Generator
Generate cryptographically secure passwords in Python using the secrets module. No third-party packages required.
The Secure Way
Python's standard library has included secrets since version 3.6, specifically for generating security-sensitive random values. It draws from os.urandom(), which uses the operating system's CSPRNG.
import secrets
import string
def generate_password(length=20, use_upper=True, use_lower=True,
use_digits=True, use_symbols=True):
pools = []
if use_upper: pools.append(string.ascii_uppercase)
if use_lower: pools.append(string.ascii_lowercase)
if use_digits: pools.append(string.digits)
if use_symbols: pools.append("!@#$%^&*()-_=+[]{}")
if not pools:
raise ValueError("At least one character type must be enabled")
all_chars = "".join(pools)
# Guarantee at least one character from each selected pool
password_chars = [secrets.choice(pool) for pool in pools]
# Fill the rest from the combined pool
password_chars += [secrets.choice(all_chars) for _ in range(length - len(pools))]
# Shuffle so the guaranteed characters aren't always first
secrets.SystemRandom().shuffle(password_chars)
return "".join(password_chars)
print(generate_password(20))
# e.g. 'k7$Rt2Xz!mQ9wLp#4vNc'
The Wrong Way (Don't Do This)
random module for passwords, tokens, or keys. It uses the Mersenne Twister algorithm, which is fast and high-quality for simulations, but fully deterministic and not designed to resist an attacker who is actively trying to predict it.
import random
import string
# INSECURE: random.choice() is not cryptographically secure
password = "".join(random.choice(string.ascii_letters) for _ in range(16))
If an attacker can observe roughly 624 consecutive outputs of Python's random module, they can reconstruct its entire internal state and predict every subsequent "random" value it produces, including any password generated afterward. This is a well-known, practical attack, not a theoretical one.
Generating a Simple Token or PIN
For URL-safe tokens (e.g. password reset links) or numeric PINs, secrets has dedicated helpers:
import secrets
# URL-safe random token, e.g. for password reset links
token = secrets.token_urlsafe(32)
# Numeric PIN
pin = "".join(secrets.choice("0123456789") for _ in range(6))
Common Mistakes
- Using
random.seed()anywhere near password generation. Seeding makes output reproducible, which is the opposite of what a password needs. - Building the character pool with duplicates removed incorrectly, which can silently bias character frequency. Keep pools as plain concatenated strings, as shown above.
- Not guaranteeing at least one of each required character type. With pure random sampling over a mixed pool, it's statistically possible (if unlikely) to draw zero symbols even with symbols enabled. Explicitly guarantee one character per active pool, as in the example.
- Logging or printing generated passwords in application logs. Treat them the same as any other secret, and never write them to persistent logs.
Frequently Asked Questions
The random module uses the Mersenne Twister PRNG, which is deterministic and can be reverse-engineered from its outputs. The secrets module (Python 3.6+) draws from the OS's cryptographically secure random source and is the documented, correct choice for passwords, tokens and keys.
Both select a random element from a sequence with the same signature, but secrets.choice() is backed by os.urandom() while random.choice() is backed by the Mersenne Twister. Only secrets.choice() should be used for anything security-sensitive.
Draw one required character from each enabled pool first, fill the rest from the combined pool, then shuffle with secrets.SystemRandom().shuffle(). Never use Python's plain random.shuffle() for this.
Don't want to write the code?
Generate a strong password instantly with our browser-based generator: same principle, zero setup.
Use the Password Generator โ