Skip to content

Random Password Generator

Abstract

Most “password generator” tutorials online quietly teach you to write an insecure one. They use Python’s randomrandom module — which is not cryptographically secure. In this tutorial we will build the basic version first (so you understand the parts), then fix it with the right module (secretssecrets), then go further with entropy calculations, customization flags, clipboard copy, a passphrase mode, and a Tkinter GUI.

You will learn:

  • Why randomrandom is wrong for passwords and secretssecrets is right.
  • How to combine character sets (letters, digits, punctuation) cleanly.
  • How to guarantee that every required character type is present.
  • How to compute the entropy of a password and what that number means.
  • How to copy a generated password to the clipboard and clear it after a delay.
  • How to wrap the whole thing in a tiny GUI.

The randomrandom vs. secretssecrets Trap

Almost every online tutorial uses:

insecure.py
import random
password = ''.join(random.choice(charset) for _ in range(length))   # ❌
insecure.py
import random
password = ''.join(random.choice(charset) for _ in range(length))   # ❌

The randomrandom module uses the Mersenne Twister PRNG. It is fast, statistically uniform, and completely predictable if you know its state. Given enough output an attacker can reconstruct the seed and predict every future call.

For anything security-related — passwords, tokens, session IDs, OTP codes — use secretssecrets:

secure.py
import secrets, string
charset = string.ascii_letters + string.digits + string.punctuation
password = ''.join(secrets.choice(charset) for _ in range(length))   # ✅
secure.py
import secrets, string
charset = string.ascii_letters + string.digits + string.punctuation
password = ''.join(secrets.choice(charset) for _ in range(length))   # ✅

The secretssecrets module is a thin wrapper over your OS’s cryptographic randomness source (/dev/urandom/dev/urandom on Unix, CryptGenRandomCryptGenRandom on Windows). It is what every standard-library example for cryptography uses.

Prerequisites

  • Python 3.6 or above (secretssecrets was added in 3.6).
  • A code editor or IDE.
  • Familiarity with strings, loops, and the input()input() function.

Getting Started

Create the project

  1. Create a folder named random-password-generatorrandom-password-generator.
  2. Inside it, create randompasswordgenerator.pyrandompasswordgenerator.py.

Write the code

Random Password GeneratorSource
Random Password Generator
# Random Password Generator
 
import random
import string
 
def randompasswordgenerator():
    print("Random Password Generator")
    print("Enter the length of password: ")
    length = int(input())
    password = ''
    for i in range(length):
        password += random.choice(string.ascii_letters + string.digits + string.punctuation)
    print(password)
    
if __name__ == "__main__":
    randompasswordgenerator()
Random Password Generator
# Random Password Generator
 
import random
import string
 
def randompasswordgenerator():
    print("Random Password Generator")
    print("Enter the length of password: ")
    length = int(input())
    password = ''
    for i in range(length):
        password += random.choice(string.ascii_letters + string.digits + string.punctuation)
    print(password)
    
if __name__ == "__main__":
    randompasswordgenerator()

Run it

command
C:\Users\Your Name\random-password-generator> python randompasswordgenerator.py
Random Password Generator
Length: 16
aB3#dE7&hI9@kL2$
command
C:\Users\Your Name\random-password-generator> python randompasswordgenerator.py
Random Password Generator
Length: 16
aB3#dE7&hI9@kL2$

Step-by-Step Explanation

1. Pick the right module

randompasswordgenerator.py
import secrets
import string
randompasswordgenerator.py
import secrets
import string
  • secretssecrets for randomness.
  • stringstring provides ready-made character classes (ascii_lettersascii_letters, digitsdigits, punctuationpunctuation).

2. Build the character set

randompasswordgenerator.py
ALPHABET = string.ascii_letters + string.digits + string.punctuation
randompasswordgenerator.py
ALPHABET = string.ascii_letters + string.digits + string.punctuation

Concatenated strings — Python treats this as one big string of characters from which to pick.

3. Generate

randompasswordgenerator.py
def generate(length: int = 16) -> str:
    return "".join(secrets.choice(ALPHABET) for _ in range(length))
randompasswordgenerator.py
def generate(length: int = 16) -> str:
    return "".join(secrets.choice(ALPHABET) for _ in range(length))

A generator expression inside "".join(...)"".join(...) is the idiomatic way to build a string of N characters.

4. Read the length and print

randompasswordgenerator.py
length = int(input("Length: "))
print(generate(length))
randompasswordgenerator.py
length = int(input("Length: "))
print(generate(length))

Guarantee Variety

A naive generator may produce a password with no digits. Many password rules require at least one of each type:

guarantee.py
import secrets, string
 
def generate(length: int = 16) -> str:
    if length < 4:
        raise ValueError("length must be ≥ 4 to satisfy character-class rules")
    pools = [
        string.ascii_lowercase,
        string.ascii_uppercase,
        string.digits,
        string.punctuation,
    ]
    # One mandatory character from each pool…
    password = [secrets.choice(pool) for pool in pools]
    # …then fill the rest from the combined alphabet
    alphabet = "".join(pools)
    password += [secrets.choice(alphabet) for _ in range(length - len(pools))]
    secrets.SystemRandom().shuffle(password)   # avoid predictable ordering
    return "".join(password)
guarantee.py
import secrets, string
 
def generate(length: int = 16) -> str:
    if length < 4:
        raise ValueError("length must be ≥ 4 to satisfy character-class rules")
    pools = [
        string.ascii_lowercase,
        string.ascii_uppercase,
        string.digits,
        string.punctuation,
    ]
    # One mandatory character from each pool…
    password = [secrets.choice(pool) for pool in pools]
    # …then fill the rest from the combined alphabet
    alphabet = "".join(pools)
    password += [secrets.choice(alphabet) for _ in range(length - len(pools))]
    secrets.SystemRandom().shuffle(password)   # avoid predictable ordering
    return "".join(password)

Subtle point: do not just append one digit at the end — that is a known weakness. We shuffle the whole list so the required characters land in random positions.

Entropy: How Strong Is Strong?

Password entropy in bits = log₂(alphabet_size ** length)log₂(alphabet_size ** length).

Alphabet sizeLengthEntropy (bits)Comment
26 (lowercase only)8~38Crackable in minutes by a GPU
62 (letters + digits)12~71Reasonable
95 (full printable ASCII)16~104Strong
9520~131Excellent

A function to compute it:

entropy.py
import math
def entropy_bits(password: str) -> float:
    pool = 0
    if any(c.islower() for c in password): pool += 26
    if any(c.isupper() for c in password): pool += 26
    if any(c.isdigit() for c in password): pool += 10
    if any(c in string.punctuation for c in password): pool += len(string.punctuation)
    return len(password) * math.log2(max(pool, 1))
 
print(f"{entropy_bits(p):.1f} bits")
entropy.py
import math
def entropy_bits(password: str) -> float:
    pool = 0
    if any(c.islower() for c in password): pool += 26
    if any(c.isupper() for c in password): pool += 26
    if any(c.isdigit() for c in password): pool += 10
    if any(c in string.punctuation for c in password): pool += len(string.punctuation)
    return len(password) * math.log2(max(pool, 1))
 
print(f"{entropy_bits(p):.1f} bits")

The conventional wisdom: aim for ≥ 80 bits for important accounts, ≥ 128 bits for master passwords / vault keys.

Avoid Ambiguous Characters

Some characters are easy to confuse: 00 / OO, 11 / ll / II. Let the user opt out:

no_ambig.py
AMBIGUOUS = "0Oo1lI"
def generate(length, no_ambig=False):
    pool = ALPHABET
    if no_ambig:
        pool = "".join(c for c in pool if c not in AMBIGUOUS)
    return "".join(secrets.choice(pool) for _ in range(length))
no_ambig.py
AMBIGUOUS = "0Oo1lI"
def generate(length, no_ambig=False):
    pool = ALPHABET
    if no_ambig:
        pool = "".join(c for c in pool if c not in AMBIGUOUS)
    return "".join(secrets.choice(pool) for _ in range(length))

Copy to Clipboard

Generated passwords are easier to use if they land directly in the clipboard. Two options:

install
pip install pyperclip
install
pip install pyperclip
clipboard.py
import pyperclip
pyperclip.copy(password)
print("Copied to clipboard.")
clipboard.py
import pyperclip
pyperclip.copy(password)
print("Copied to clipboard.")

For extra hygiene, clear the clipboard after 30 seconds:

clear.py
import threading
def clear_later():
    pyperclip.copy("")
threading.Timer(30, clear_later).start()
clear.py
import threading
def clear_later():
    pyperclip.copy("")
threading.Timer(30, clear_later).start()

Common Mistakes

ProblemCauseFix
Uses random.choicerandom.choiceInsecure PRNGSwitch to secrets.choicesecrets.choice
Required character placed at index 0 every timeBuilt the password by appending required charactersShuffle after assembly
int(input())int(input()) crashes on lettersNo validationWrap with try/excepttry/except
Password always rejected by the siteMissing a required character classUse the “guarantee variety” version
Same password generated twice in a scriptReused a seeded randomrandomNever seed secretssecrets (it is not seedable)

Variations to Try

1. Passphrase mode (Diceware)

Long, memorable, and high-entropy. Five random words from a word list of 7,776 is ~64 bits already.

passphrase.py
import secrets, pathlib
WORDS = pathlib.Path("eff_wordlist.txt").read_text().splitlines()
def passphrase(n=5):
    return "-".join(secrets.choice(WORDS) for _ in range(n))
passphrase.py
import secrets, pathlib
WORDS = pathlib.Path("eff_wordlist.txt").read_text().splitlines()
def passphrase(n=5):
    return "-".join(secrets.choice(WORDS) for _ in range(n))

EFF publishes a free word list specifically for this purpose.

2. Multiple passwords at once

batch.py
n = int(input("How many? "))
for _ in range(n):
    print(generate(16))
batch.py
n = int(input("How many? "))
for _ in range(n):
    print(generate(16))

3. Strength meter

Reuse the entropy function above; rate as Weak / OK / Strong / Excellent.

4. CLI flags with argparse

cli
python randompasswordgenerator.py --length 20 --no-ambig --count 5
cli
python randompasswordgenerator.py --length 20 --no-ambig --count 5

5. GUI version

gui.py
import tkinter as tk
from tkinter import ttk
def gen():
    out_var.set(generate(int(length_var.get())))
root = tk.Tk()
length_var = tk.StringVar(value="16")
out_var = tk.StringVar()
tk.Entry(root, textvariable=length_var).pack()
tk.Button(root, text="Generate", command=gen).pack()
tk.Entry(root, textvariable=out_var, width=40).pack()
root.mainloop()
gui.py
import tkinter as tk
from tkinter import ttk
def gen():
    out_var.set(generate(int(length_var.get())))
root = tk.Tk()
length_var = tk.StringVar(value="16")
out_var = tk.StringVar()
tk.Entry(root, textvariable=length_var).pack()
tk.Button(root, text="Generate", command=gen).pack()
tk.Entry(root, textvariable=out_var, width=40).pack()
root.mainloop()

6. Save to an encrypted vault

Append generated passwords (with a label) to an encrypted file using cryptography.fernetcryptography.fernet. See Personal Diary for the same encryption idea.

7. QR-code output

Use pip install qrcodepip install qrcode to render the password as a QR code you can scan to a phone.

8. Check against breach corpora

Send a SHA-1 prefix of the password to Have I Been Pwned’s k-anonymity API — if it appears in known breaches, regenerate.

Best Practices for Password Generation

  • Use secretssecrets. Always. Forever.
  • Length matters more than complexity. A long passphrase beats a short symbol soup.
  • Guarantee character classes when sites demand them; do not hope randomness gives you one.
  • Shuffle after assembly to avoid predictable positions.
  • Never log generated passwords anywhere (stdout in a CI run, error message, debug print).
  • Copy via clipboard, do not save to a plain file. If you must persist, encrypt.

Common Use Cases

  • New account signups.
  • API key / token generation.
  • One-time temporary passwords for password resets.
  • Database / service credentials (developer ergonomics + safety).
  • Wi-Fi guest passwords printed on a slip.

Educational Value

  • Cryptographic randomness — why randomrandom is the wrong tool.
  • Entropy — turning intuition about password strength into a number.
  • String composition — building from character classes.
  • CLI ergonomics — flags, validation, clipboard integration.
  • Security mindset — small choices like “shuffle after assembly” matter.

Next Steps

  • Switch to secretssecrets everywhere immediately.
  • Add clipboard copy with auto-clear.
  • Add a passphrase mode using the EFF word list.
  • Integrate with Have I Been Pwned for compromise checks.
  • Wrap in a GUI or build it into a real password manager (encrypted vault, master password, search).

Conclusion

You generated random passwords, then learned why “random” is the wrong word — and rebuilt the generator with secretssecrets. You measured entropy, guaranteed variety, copied to the clipboard, and have a roadmap to a real password manager. Security is full of these small, mostly-invisible choices. The full source is on GitHub. Find more security-focused projects on Python Central Hub.

If this helped you, consider buying me a coffee ☕

Buy me a coffee

Was this page helpful?

Let us know how we did