🐍 How to Generate Secure Passwords in Python: Dev Guide
On this page
Generating secure passwords in Python requires more than just calling random.choice() on a character set. The standard random module uses a Mersenne Twister PRNG that is unsuitable for cryptographic applications. Python's secrets module, introduced in Python 3.6 and hardened through subsequent releases, provides the CSPRNG (Cryptographically Secure Pseudorandom Number Generator) access that developers need for password generation, token creation, and credential management.
As the NIST SP 800-63B digital identity guidelines state, memorised secrets must be generated using an approved random bit generator. The OWASP Cheat Sheet Series explicitly warns against using random for security-critical functions. In this developer's guide, you'll learn production-ready patterns for generating secure passwords in Python, from simple CLI scripts to scalable API endpoints.
Why Python's random Module Is Not Suitable
Before we write any code, it's important to understand why the standard random module must never be used for password generation. The random module implements the Mersenne Twister (MT19937) algorithm, which is deterministic and predictable if an attacker can observe enough outputs. The Verizon 2026 DBIR notes that predictable password generation has contributed to multiple high-profile credential stuffing attacks.
# NEVER DO THIS — predictable output
import random
password = ''.join(random.choice(string.ascii_letters + string.digits)
for _ in range(16)) # Predictable!
The secrets module, by contrast, uses the operating system's CSPRNG (/dev/urandom on Linux, CryptGenRandom on Windows). This provides:
- Unpredictability: Output cannot be reconstructed, even with knowledge of prior outputs
- Thread safety: Safe for concurrent use in multi-threaded applications
- Entropy source: Draws from hardware entropy sources where available
- FIPS compliance: Meets FIPS 140-2 requirements when used with FIPS-enabled OpenSSL
Basic Password Generation with secrets
The simplest approach uses secrets.choice() with a character pool. Here's a production-ready function:
import secrets
import string
def generate_password(length: int = 20,
use_digits: bool = True,
use_punctuation: bool = True) -> str:
"""Generate a cryptographically secure random password."""
chars = string.ascii_letters
if use_digits:
chars += string.digits
if use_punctuation:
chars += string.punctuation
return ''.join(secrets.choice(chars) for _ in range(length))
This function produces a password with ~6.5 bits of entropy per character when using all pools (95 characters total). A 20-character password from this function provides approximately 130 bits of entropy — well above the OWASP recommended minimum of 64 bits for general-purpose passwords.
Ensuring Character Diversity
A common problem with simple random selection is that generated passwords might lack certain character types. NIST SP 800-63B recommends that passwords include at least one character from each of these categories: uppercase, lowercase, digits, and special characters. Here's an enhanced version that guarantees diversity:
def generate_diverse_password(length: int = 20) -> str:
"""Generate a password that includes at least one character from each category."""
if length < 4:
raise ValueError("Password length must be at least 4")
# Guarantee one from each category
mandatory = [
secrets.choice(string.ascii_lowercase),
secrets.choice(string.ascii_uppercase),
secrets.choice(string.digits),
secrets.choice(string.punctuation)
]
# Fill the rest randomly
all_chars = string.ascii_letters + string.digits + string.punctuation
remaining = [secrets.choice(all_chars) for _ in range(length - 4)]
# Shuffle to avoid predictable positioning
combined = mandatory + remaining
secrets.SystemRandom().shuffle(combined)
return ''.join(combined)
This guarantees that every password contains at least one of each character type, preventing validation failures in downstream systems while maintaining full entropy.
CLI Tool for Password Generation
For developers who prefer working from the terminal, here's a complete CLI tool using Python's argparse module. This pattern integrates with shell scripts and CI/CD pipelines:
#!/usr/bin/env python3
"""genpass — Secure password generator for the command line"""
import argparse
import secrets
import string
import sys
def parse_args():
parser = argparse.ArgumentParser(
description="Generate cryptographically secure passwords")
parser.add_argument("-l", "--length", type=int, default=20,
help="Password length (default: 20)")
parser.add_argument("-n", "--no-digits", action="store_true",
help="Exclude digits")
parser.add_argument("-p", "--no-punctuation", action="store_true",
help="Exclude punctuation")
parser.add_argument("-c", "--count", type=int, default=1,
help="Number of passwords to generate")
parser.add_argument("-q", "--quiet", action="store_true",
help="Output only passwords (one per line)")
return parser.parse_args()
def main():
args = parse_args()
if args.length < 8:
sys.stderr.write("Error: Minimum password length is 8\n")
sys.exit(1)
for i in range(args.count):
password = generate_diverse_password(args.length)
if args.quiet:
print(password)
else:
entropy = args.length * 6.5
print(f"Password {i+1}: {password}")
print(f" Length: {args.length} Est. entropy: {entropy:.0f} bits")
print()
if __name__ == "__main__":
main()
Save this as genpass.py and run it:
$ python3 genpass.py -l 24 -c 3 -q
7Kf&8x!mPq@3Rz#9Lb$2Wn*
A4BjK%7LmNp@8RzX2Yq$5Wc
9FgH#3JkLm!6PqRs^7VwXyZ
Password Generation as an API Endpoint
For microservice architectures, expose password generation behind an API. The ENISA recommends all credential generation follow strict rate limiting to prevent abuse. Here's a FastAPI implementation:
from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel
from typing import Optional
import secrets
import string
app = FastAPI(title="Password Generator API")
class PasswordRequest(BaseModel):
length: int = 20
count: int = 1
include_digits: bool = True
include_punctuation: bool = True
class PasswordResponse(BaseModel):
passwords: list[str]
entropy_bits: float
@app.post("/api/generate", response_model=PasswordResponse)
async def generate_passwords(req: PasswordRequest):
if req.length < 8 or req.length > 128:
raise HTTPException(400, "Length must be 8-128")
if req.count < 1 or req.count > 100:
raise HTTPException(400, "Count must be 1-100")
passwords = []
for _ in range(req.count):
chars = string.ascii_letters
if req.include_digits:
chars += string.digits
if req.include_punctuation:
chars += string.punctuation
pwd = ''.join(secrets.choice(chars) for _ in range(req.length))
passwords.append(pwd)
entropy = req.length * (len(set(chars)) ** 0.5)
return PasswordResponse(passwords=passwords, entropy_bits=round(entropy, 1))
Rate limit this endpoint using FastAPI's middleware or a reverse proxy like Nginx. For teams needing encrypted internal communication alongside secure credentials, TrekMail offers end-to-end encrypted messaging that integrates with developer workflows. The OWASP recommends a maximum of 10 requests per IP per minute for credential generation endpoints to prevent abuse.
Token and API Key Generation
Beyond passwords, the same CSPRNG patterns work for generating secure tokens, API keys, and session identifiers:
def generate_api_key(length: int = 32) -> str:
"""Generate a URL-safe API key."""
return secrets.token_urlsafe(length)
def generate_session_id() -> str:
"""Generate a secure session identifier."""
return secrets.token_hex(32)
def generate_otp(digits: int = 6) -> str:
"""Generate a numeric OTP."""
return ''.join(str(secrets.randbelow(10)) for _ in range(digits))
The secrets.token_urlsafe() function is particularly useful for API keys because it produces URL-safe base64-encoded output with no padding characters. A 32-byte token provides 256 bits of entropy — well beyond what any brute-force attack could overcome within the lifetime of the universe.
Integration with Password Managers
While generating passwords programmatically is useful, most users benefit from using a dedicated password manager. Services like Kaspersky Password Manager use the same CSPRNG principles under the hood while providing encrypted storage, cross-device sync, and breach monitoring. For enterprise deployments, Keeper Business offers API access for automated password rotation and policy enforcement.
Common Pitfalls
- Using uuid4() for passwords: While
uuid.uuid4()uses a CSPRNG, it generates a fixed 128-bit value in a specific format (hex with hyphens). Not suitable for password generation. - Storing passwords in environment variables: Generated passwords should be stored in a secrets manager or encrypted vault, not plaintext
.envfiles. - Logging generated passwords: Never log generated passwords to stdout or application logs. Use the quiet mode for CLI tools and disable request logging for API endpoints.
- Predictable seed values: If you must use
random.Random()for deterministic testing, never expose that codepath in production. Usesecretsexclusively for production password generation.
FAQs
Why is Python's random module not suitable for password generation?
The random module uses the Mersenne Twister algorithm, which is deterministic and predictable. An attacker who observes enough outputs can reconstruct the internal state and predict future outputs. Use the secrets module instead, which draws from the operating system's CSPRNG.
What minimum password length should I use in Python?
For general web application use, generate passwords of at least 16 characters. For admin accounts or encryption keys, use 24-32 characters. For API keys and tokens, use secrets.token_urlsafe(32) for 256 bits of entropy.
How do I ensure my generated password passes validation rules?
Use the diverse generation pattern shown above that guarantees at least one character from each category (uppercase, lowercase, digits, punctuation). Then shuffle the result to avoid predictable character positioning.
Can I generate passwords securely in a Docker container?
Yes — Python's secrets module works in containers because it reads entropy from the host kernel's /dev/urandom. Containerisation does not reduce entropy quality for CSPRNG operations. For secure remote access to your development environments, Turbo VPN provides encrypted tunnels that protect your API endpoints during development and testing. Just ensure your container has sufficient entropy available (most modern Linux kernels do).
What is the best way to store generated passwords?
Never store generated passwords in plaintext. Use a secrets manager (Hashicorp Vault, AWS Secrets Manager), a password manager with API access, or encrypt them using a strong KDF like Argon2id before storage.
Affiliate Disclosure: This post may contain affiliate links. If you purchase through these links, we may earn a small commission at no extra cost to you. Full disclosure.