Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes
Opinion

Stop Using `random` for Sensitive Data: Use Python's `secrets` Module

This article argues why developers should replace Python's `random` module with `secrets` for passwords, tokens, and other security-critical tasks. It explains the cryptographic strength of `secrets`, provides code examples, and warns against common pitfalls.

July 2026 5 min read 1 views 0 hearts

Why You Should Stop Using random for Sensitive Data and Switch to Python’s secrets Module

Look, I’ll be straight with you: if you’re using Python’s random module to generate passwords, tokens, or anything security-related, you’re doing it wrong. I know, because I used to do it too. It’s not your fault—random is easy, intuitive, and works fine for games and simulations. But here’s the thing: random is predictable. Really predictable. And that’s a problem when you’re dealing with sensitive data.

The secrets module, introduced in Python 3.6, is built specifically for this purpose. It uses cryptographically strong random number generators, meaning the output is virtually impossible to predict—even if someone knows the algorithm. It’s designed for passwords, security tokens, and anything that needs to withstand attack.

What Makes secrets Different?

Under the hood, secrets uses your operating system’s secure random number generator (like /dev/urandom on Linux or CryptGenRandom on Windows). These sources are designed to be unpredictable, pulling from hardware entropy sources like mouse movements, keyboard timing, and other system noise.

In contrast, random uses a pseudo-random number generator (specifically Mersenne Twister) that’s deterministic. Give it the same seed, and you’ll get the same sequence of “random” numbers. That’s fine for Monte Carlo simulations—not for security tokens.

Getting Started with secrets

Let’s say you need to generate a random password for a user at PythonSkillset.com. Here’s the old way (don’t do this):

import random
import string

# DON'T DO THIS
password = ''.join(random.choices(string.ascii_letters + string.digits, k=12))

Now here’s the secure version using secrets:

import secrets
import string

# DO THIS INSTEAD
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
password = ''.join(secrets.choice(alphabet) for _ in range(16))
print(password)  # Something like 'Gx$7#kLp@2mN9qR!'

The secrets.choice() function does exactly what random.choice() does—except securely. For a 16-character password using 72 possible characters, you get about 95 bits of entropy. That’s plenty strong.

Real-World Use Cases at PythonSkillset

1. Generating API Tokens

If you’re building an API that hands out authentication tokens, secrets is your friend:

import secrets

def generate_api_token():
    return secrets.token_hex(32)  # 64-character hex string

# Usage
new_token = generate_api_token()
# Result: 'a1b2c3d4e5f6...' (64 characters of hex)

The token_hex() method is perfect here. Each byte gets converted to two hex characters, so 32 bytes = 64 characters. That’s 256 bits of entropy—essentially unbreakable.

2. Creating Password Reset Links

When a user forgets their password, you need a unique, unpredictable URL:

import secrets
import hashlib

def generate_reset_token(user_id):
    raw_token = secrets.token_urlsafe(32)
    # Store hashed version in database for security
    hashed_token = hashlib.sha256(raw_token.encode()).hexdigest()
    return raw_token, hashed_token

# In practice:
token, hashed = generate_reset_token(12345)
# token = 'some-base64-url-safe-string'
# Store 'hashed' in DB, email 'token' to user

Notice I used token_urlsafe()—it generates base64-encoded strings that are URL-safe (no +, /, or = characters). Perfect for embedding in links.

3. Generating Session Keys

For web applications, session management is critical:

import secrets

def create_session_id():
    return secrets.token_hex(16)  # 32-character session ID

# Or even better, use token_bytes for raw entropy
session_key = secrets.token_bytes(16)  # 16 random bytes

Common Pitfalls to Avoid

Mistake 1: Using secrets with random.seed()

# WRONG: Don't seed secrets
secrets.random.seed(42)  # This doesn't work anyway

The secrets module doesn’t let you set a seed—and for good reason. If you could seed it, it would be predictable.

Mistake 2: Using secrets.choice() on small sequences

# WEAK: Only 10 possible values
token = ''.join(secrets.choice('0123456789') for _ in range(4))
# Only 10,000 possible combinations

Always use large enough character sets. For a 4-digit PIN, you’re limited to 10,000 combinations. For a 16-character password, use 72+ characters.

Mistake 3: Storing plaintext tokens

# WRONG: Don't store raw tokens
session['token'] = secrets.token_hex(16)  # Never do this

# RIGHT: Store hashed version
hashed = hashlib.sha256(raw_token.encode()).hexdigest()

When to Use secrets vs random

Use Case Module Reason
Password generation secrets Predictability matters
Session tokens secrets Must be unguessable
API keys secrets Security critical
Monte Carlo simulation random Speed over security
Game dice rolls random Perfectly fine
Shuffling deck of cards random No security implications

A Quick Performance Note

Yes, secrets is slower than random. Generating 10,000 random passwords with random takes about 0.1 seconds; with secrets, maybe 0.3 seconds. But when generating sensitive data, you generate a handful at a time—the performance difference is negligible.

Wrapping It Up

At PythonSkillset.com, we’ve seen too many developers use random for security-critical tasks and realize their mistake only after a breach. The secrets module is simple, built-in, and purpose-built for this exact use case. It takes five minutes to replace random.choice() with secrets.choice() in your codebase—and that five minutes could save you a world of trouble.

Start using secrets today. Your users—and your security team—will thank you.

Comments

Questions, corrections, and tips stay visible for everyone reading this page.

0 in thread

Join the discussion

Shown next to your comment.

Up to 4,000 characters

No comments yet

Be the first to leave a note — it helps the next reader.