Engineering

🐍 How to Generate Secure Passwords in Python: Dev Guide

By Ateeq Y Tanoli, · 3 June 2026 · 6 min read · 1,306 words

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:

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

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.

Generate a Free Strong Password →

More Password Security Tools

🔑 SecureKeyGen⚔️ TitanPasswords🛡️ Best Password Generator🔐 Free Strong Password⚡ Instant Password🗝️ Iron Vault Keys👨‍👩‍👧‍👦 Safe Pass Builder🛡️ Trusty Password⚙️ StrongPassFactory🔑 SecureKeyGen.org📚 TrustyPassword.org
We use cookies to improve your experience. Learn more