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
randomrandomis wrong for passwords andsecretssecretsis 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:
import random
password = ''.join(random.choice(charset) for _ in range(length)) # ❌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:
import secrets, string
charset = string.ascii_letters + string.digits + string.punctuation
password = ''.join(secrets.choice(charset) for _ in range(length)) # ✅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 (
secretssecretswas added in 3.6). - A code editor or IDE.
- Familiarity with strings, loops, and the
input()input()function.
Getting Started
Create the project
- Create a folder named
random-password-generatorrandom-password-generator. - Inside it, create
randompasswordgenerator.pyrandompasswordgenerator.py.
Write the code
Random Password Generator
Source# 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
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
C:\Users\Your Name\random-password-generator> python randompasswordgenerator.py
Random Password Generator
Length: 16
aB3#dE7&hI9@kL2$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
import secrets
import stringimport secrets
import stringsecretssecretsfor randomness.stringstringprovides ready-made character classes (ascii_lettersascii_letters,digitsdigits,punctuationpunctuation).
2. Build the character set
ALPHABET = string.ascii_letters + string.digits + string.punctuationALPHABET = string.ascii_letters + string.digits + string.punctuationConcatenated strings — Python treats this as one big string of characters from which to pick.
3. Generate
def generate(length: int = 16) -> str:
return "".join(secrets.choice(ALPHABET) for _ in range(length))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
length = int(input("Length: "))
print(generate(length))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:
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)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 size | Length | Entropy (bits) | Comment |
|---|---|---|---|
| 26 (lowercase only) | 8 | ~38 | Crackable in minutes by a GPU |
| 62 (letters + digits) | 12 | ~71 | Reasonable |
| 95 (full printable ASCII) | 16 | ~104 | Strong |
| 95 | 20 | ~131 | Excellent |
A function to compute it:
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")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:
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))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:
pip install pyperclippip install pyperclipimport pyperclip
pyperclip.copy(password)
print("Copied to clipboard.")import pyperclip
pyperclip.copy(password)
print("Copied to clipboard.")For extra hygiene, clear the clipboard after 30 seconds:
import threading
def clear_later():
pyperclip.copy("")
threading.Timer(30, clear_later).start()import threading
def clear_later():
pyperclip.copy("")
threading.Timer(30, clear_later).start()Common Mistakes
| Problem | Cause | Fix |
|---|---|---|
Uses random.choicerandom.choice | Insecure PRNG | Switch to secrets.choicesecrets.choice |
| Required character placed at index 0 every time | Built the password by appending required characters | Shuffle after assembly |
int(input())int(input()) crashes on letters | No validation | Wrap with try/excepttry/except |
| Password always rejected by the site | Missing a required character class | Use the “guarantee variety” version |
| Same password generated twice in a script | Reused a seeded randomrandom | Never 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.
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))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
n = int(input("How many? "))
for _ in range(n):
print(generate(16))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
python randompasswordgenerator.py --length 20 --no-ambig --count 5python randompasswordgenerator.py --length 20 --no-ambig --count 55. GUI version
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()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
randomrandomis 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
secretssecretseverywhere 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 coffeeWas this page helpful?
Let us know how we did
