PPasswordForge
Get PasswordForge

PasswordForge/Guides

Code a Secure Password Generator in Python

Learn to build a Python password generator using secrets module. See code examples for random strings, entropy calculation, and bulk export.

October 31, 2026 · 4 min read

A secure Python password generator relies on the secrets module instead of random to ensure cryptographic strength, and uses math.log2 to calculate actual entropy based on the character set size and password length. To build one, define your character pool, use secrets.choice() to pick characters, and calculate entropy as length * log2(charset_size).

Why Math.random() Fails for Security

Most beginners start with Python’s random module because it is easy to import and use. However, random uses a Mersenne Twister pseudo-random number generator. While excellent for simulations or games, it is not cryptographically secure. If an attacker observes enough output, they can predict future values. For passwords, which must remain unpredictable to anyone but the user, you need a Cryptographically Secure Pseudo-Random Number Generator (CSPRNG).

In web browsers, this distinction is critical. Using Math.random() in JavaScript produces weak passwords because it is not seeded with enough system entropy. Secure web tools rely on crypto.getRandomValues(), which pulls randomness from the operating system’s kernel. Python mirrors this architecture. You must avoid random and use the module designed specifically for security-sensitive applications.

The Right Tool: Python's Secrets Module

Python introduced the secrets module in version 3.6 specifically to handle security-sensitive randomness. It provides functions like secrets.choice(), secrets.randbelow(), and secrets.token_hex(). Under the hood, secrets uses the same system-level entropy sources that secure web browsers use for TLS keys. This ensures that each character selected is independent and unpredictable.

When building a generator, you rarely need the more complex functions like token_urlsafe. The core requirement is selecting characters from a defined pool without bias. secrets.choice() does exactly this. It selects one element from a non-empty sequence uniformly at random. This is the foundation of any robust local password generator.

Building the Basic Generator

Start by defining the character sets. A strong password usually combines lowercase letters, uppercase letters, digits, and symbols. You can import these directly from Python’s string module to avoid typos. Then, concatenate them into a single string representing your alphabet.

Next, write a function that takes a desired length and returns a string. Loop through the desired length, calling secrets.choice() on your alphabet string for each iteration. Join the resulting list of characters into a single string. This simple loop ensures every character is drawn independently from the secure RNG.

import secrets
import string

def generate_password(length=16):
    alphabet = string.ascii_letters + string.digits + "!@#$%^&*()"
    password = ''.join(secrets.choice(alphabet) for _ in range(length))
    return password

This code is minimal but functional. It guarantees that every character is chosen via a CSPRNG. However, it does not yet tell you how strong the password is. A 16-character password from a small set is weaker than a 16-character password from a large set. You need to quantify this strength.

Adding Entropy Calculation

Entropy measures the unpredictability of a password. It is expressed in bits. The formula for a random password is length * log2(charset_size). If your alphabet has 62 characters (26 lowercase + 26 uppercase + 10 digits), the log2 of 62 is approximately 5.95 bits per character. A 12-character password from this set has roughly 71 bits of entropy.

You can calculate this dynamically in Python using the math module. Pass the length of your alphabet string to math.log2() and multiply by the password length. This gives you the theoretical maximum entropy. Real-world entropy may be slightly lower if users impose constraints, but for a purely random generator, this calculation is accurate.

import math

def calculate_entropy(password, alphabet):
    charset_size = len(alphabet)
    if charset_size == 0:
        return 0
    return len(password) * math.log2(charset_size)

Displaying this number helps users understand why longer passwords are better. It mirrors the entropy display found in PasswordForge, which shows actual bits rather than vague "strong" or "weak" labels. Knowing that a 20-character password from a 94-character set offers over 130 bits of entropy gives confidence in its strength.

Bulk Generation and CSV Export

Generating one password is useful for testing, but credential rotation often requires dozens or hundreds of passwords at once. You can extend the basic function to generate a list. Use a list comprehension to call the generator multiple times.

To make the data usable, export it to a CSV file. This format is compatible with most password managers and spreadsheets. Include columns for the password itself, its length, and its calculated entropy. This metadata allows you to verify strength before importing into a vault.

import csv

def bulk_generate(count=50, length=16):
    alphabet = string.ascii_letters + string.digits + "!@#$%^&*()"
    rows = []
    for _ in range(count):
        pwd = generate_password(length)
        # Recalculate entropy using the same alphabet definition
        entropy = calculate_entropy(pwd, alphabet)
        rows.append([pwd, length, round(entropy, 2)])
    
    with open('passwords.csv', 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerow(['Password', 'Length', 'Entropy_Bits'])
        writer.writerows(rows)
    print(f"Generated {count} passwords and exported to passwords.csv")

This script generates 50 passwords, calculates their entropy, and writes them to passwords.csv. The output file will contain rows like aB3$kL9@zQ2!mN8p, 16, 98.72. You can now import this CSV into your preferred password manager or review it offline.

Validating with a Breach Check

Even a cryptographically secure password can be weak if it has appeared in a known data breach. Checking against breach databases locally is possible using a technique called k-anonymity. You hash the password with SHA-1, then send only the first five characters of the hash to a breach API. The API returns a list of hash suffixes that have been breached. If the rest of your hash matches any of those suffixes, the password is compromised.

In Python, you can use hashlib for SHA-1 and requests for the API call. This process never sends the full password, only the prefix. For local-only verification, you can skip the API and check against a local list of common passwords, but the k-anonymity method is standard for real-time checks.

import hashlib
import requests

def is_breached(password):
    sha1 = hashlib.sha1(password.encode('utf-8')).hexdigest().upper()
    prefix = sha1[:5]
    suffix = sha1[5:]
    # Note: Requires internet access to breach database
    response = requests.get(f"https://api.pwnedpasswords.com/range/{prefix}")
    return suffix in response.text

Combine this with your generator. After creating a password, check it. If it is breached, regenerate it. This loop ensures you only keep strong, unseen passwords. This mirrors the offline breach check capability in PasswordForge, which runs client-side to protect privacy while ensuring validity.

By using secrets for randomness, math.log2 for entropy, and csv for export, you build a tool that is both secure and practical. It replicates the core logic of professional generators while giving you full control over the code and data.

Do it in PasswordForge

Everything in this guide works in the browser — open the tool and try it on your own input.

Open PasswordForge →

Questions people also ask

Is Python's random module secure for passwords?

No, it is not secure for cryptographic purposes because it uses a Mersenne Twister pseudo-random number generator that can be predicted if enough output is observed. You should use the `secrets` module instead, which provides a Cryptographically Secure Pseudo-Random Number Generator (CSPRNG) backed by system-level entropy.

How do I calculate password entropy in Python?

Calculate entropy by multiplying the password length by the base-2 logarithm of the character set size using `math.log2`. For example, a 12-character password from a 62-character alphabet yields approximately 71 bits of entropy, providing a precise measure of unpredictability rather than vague strength labels.

Can I generate Diceware passphrases in Python?

Yes, you can generate Diceware passphrases by using `secrets.choice()` to select words from a predefined dictionary list rather than individual characters. This approach leverages the same secure randomness as character-based generators but relies on word-level entropy, which is often easier for users to remember while maintaining high security.

How do I export bulk passwords to CSV in Python?

Use Python's built-in `csv` module to write generated passwords along with metadata like length and entropy to a file. You can generate a list of passwords using a list comprehension and then iterate through them to write rows, ensuring the output is compatible with most password managers and spreadsheets.

More guides