Skip to contentSkip to navigation
MODULE 11 · LESSON 05

Random Numbers & Cryptographic Secrets

Generate reproducible test distributions with random and produce secure tokens with the secrets module.

Core Learning Milestones:
random.seed()
Deterministic reruns
choices() / sample()
With/without replacement
secrets.token_urlsafe()
CSPRNG tokens
compare_digest()
Constant-time defense
Estimated Time
30 Minutes
Comprehensive deep dive
Difficulty Level
Intermediate
Builds on Functions
Knowledge Check
15 Questions
+35 XP Potential
01 / Foundations

PRNGs vs CSPRNGs: When to Use random vs secrets

Python ships two standard library modules for randomness, with completely different threat models. random uses the Mersenne Twister PRNG — fast, seedable, and reproducible, but predictable after observing 624 consecutive outputs. secrets sources entropy directly from the OS CSPRNG, cannot be seeded, and is safe for security-sensitive values.

Security Invariant: never use random for passwords, API keys, session tokens, or encryption material — reach for secrets instead.

Predictable token (never do this)
import random token = str(random.randint(100000, 999999)) # guessable!
Cryptographically secure token
import secrets token = secrets.token_urlsafe(32) # unguessable
Think of it like this:

random is rolling plastic dice for a board game; secrets is drawing a cryptographic key inside a fortified bank vault.

Full Contract in Action

Contrasting PRNG Output with Cryptographic Tokens

Run the snippet below to contrast a deterministic simulation roll against a cryptographically secure token:

random_vs_secrets_intro.py — Press 'Run Code' to execute in your browser
import random
import secrets

# 1. Non-cryptographic simulation (random)
random.seed(42)
roll = random.randint(1, 6)
print("Deterministic dice roll (seed=42):", roll)

# 2. Cryptographically secure token (secrets)
api_token = secrets.token_hex(16)        # 32 hex characters
session_id = secrets.token_urlsafe(24)    # URL-safe Base64 token
print("Secure API Token:", api_token)
print("Secure Session ID:", session_id)
Randomness Glossary: Core Concepts
random.seed()Reproducibility

Initializes the PRNG deterministically for reproducible tests and simulations.

choices() / sample()Sampling

Select with replacement and weights, or without replacement for guaranteed-unique picks.

secrets.token_urlsafe()Security

Generates a cryptographically unguessable, URL-safe Base64 token from OS entropy.

compare_digest()Defense

Compares two secrets in constant time, immune to timing-attack byte leakage.

02 / Randomness & Security Tracer

Trace How Each Technique Works

Click through each technique to see its exact behavior:

random.seed() — Deterministic Reproducibility
random.seed(42) random.randint(1, 6) # always 6 with this seed

Seeding the Mersenne Twister PRNG makes every subsequent draw deterministic — essential for reproducible tests and simulations, but never for security.

03 / random vs secrets Reference

PRNG, Sampling, and CSPRNG Constructs

These three families cover simulation randomness, selection protocols, and cryptographic security.

random — PRNG (simulations, never security)

random.seed(value)

Initializes the Mersenne Twister PRNG deterministically.

random.random()

Returns a float uniformly in [0.0, 1.0).

random.randint(a, b)

Random integer N with a <= N <= b, inclusive of both endpoints.

random.randrange(a, b)

Mirrors range(): excludes the stop boundary [a, b).

Sampling & shuffling

random.choice(seq)

Returns a single random element from a non-empty sequence.

random.choices(pop, weights=None, k=n)

Samples k elements with replacement; supports relative weights.

random.sample(pop, k=n)

Samples k unique elements without replacement.

random.shuffle(x)

Mutates a list in place via Fisher-Yates shuffling.

secrets — CSPRNG (tokens, passwords, keys)

secrets.token_hex(nbytes)

Cryptographically secure hex-string token, 2×nbytes characters.

secrets.token_urlsafe(nbytes)

Cryptographically secure URL-safe Base64 token.

secrets.compare_digest(a, b)

Constant-time comparison that defeats timing attacks.

04 / Hands-on Examples

Worked Hands-on Walkthroughs

Progress step-by-step from contrasting PRNG output with secure tokens, through reproducible simulations, to constant-time verification:

Walkthrough 01 · FoundationLevel 1 · Foundation

Contrasting PRNG Output with Cryptographic Tokens

random vs secrets
Problem Scenario

secrets sources entropy directly from the OS kernel, ensuring generated tokens cannot be reverse-engineered.

Executable Solution
Contrasting PRNG Output with Cryptographic Tokens
import random
import secrets

# 1. Non-cryptographic simulation (random)
random.seed(42)
roll = random.randint(1, 6)
print("Deterministic dice roll (seed=42):", roll)

# 2. Cryptographically secure token (secrets)
api_token = secrets.token_hex(16)        # 32 hex characters
session_id = secrets.token_urlsafe(24)    # URL-safe Base64 token
print("Secure API Token:", api_token)
print("Secure Session ID:", session_id)
Step-by-Step Explanation

secrets sources entropy directly from the OS kernel, ensuring generated tokens cannot be reverse-engineered.

Walkthrough 02 · Building UpLevel 2 · Applied

Reproducible Simulation Runs with random.seed()

Monte Carlo
Problem Scenario

random.seed() ensures numerical experiments and unit tests execute with perfect deterministic consistency.

Executable Solution
Reproducible Simulation Runs with random.seed()
import random

def run_monte_carlo_trial(seed_val, iterations=1000):
    random.seed(seed_val)
    inside_circle = 0
    for _ in range(iterations):
        x, y = random.random(), random.random()
        if x**2 + y**2 <= 1.0:
            inside_circle += 1
    pi_estimate = round((4 * inside_circle) / iterations, 4)
    return pi_estimate

# Independent runs with identical seeds produce identical results!
run1 = run_monte_carlo_trial(12345)
run2 = run_monte_carlo_trial(12345)
print("Run 1 estimate:", run1)
print("Run 2 estimate:", run2)
print("Deterministic equality:", run1 == run2)
Step-by-Step Explanation

random.seed() ensures numerical experiments and unit tests execute with perfect deterministic consistency.

Walkthrough 03 · MasteryLevel 3 · Architecture

Secure Token Generation and Constant-Time Verification

Timing-Safe Compare
Problem Scenario

secrets.compare_digest executes in constant time regardless of where string mismatches occur, preventing timing leakage.

Executable Solution
Secure Token Generation and Constant-Time Verification
import secrets

def generate_password_reset_token():
    # Generate a high-entropy 32-byte URL-safe token
    return secrets.token_urlsafe(32)

def verify_reset_token(stored_token, submitted_token):
    # Constant-time comparison: immune to timing attacks!
    return secrets.compare_digest(stored_token, submitted_token)

real_token = generate_password_reset_token()
print("Generated Token:", real_token)

# Verification test
assert verify_reset_token(real_token, real_token) is True
assert verify_reset_token(real_token, "invalid_token_attempt") is False
print("Token verification passed securely!")
Step-by-Step Explanation

secrets.compare_digest executes in constant time regardless of where string mismatches occur, preventing timing leakage.

05 / Traps to Avoid

Common Traps & How to Avoid Them

Study the mistakes and clean fixes below before relying on randomness or security tokens in production:

!Trap 1: Syntax or Name Typos in Randomness & Cryptographic Secrets
Common Trap
The Bug (Avoid)Buggy Code
# Mistyped variable or missing closing parenthesis result = randomness___cryptographic_secrets_val
What Happens:
NameError: name is not defined
The Clean FixCorrected Code
# Declare and verify variables before execution val = 10 print("Defined correctly:", val)
Recommended Practice:
Never use random for security-sensitive values — always reach for secrets.token_urlsafe() or secrets.token_hex().
Why It Happens

Beginners frequently encounter typos or forgotten definitions before using an identifier.

!Trap 2: Unintended State Mutation or Scope Leakage
Subtle Trap
The Bug (Avoid)Buggy Code
# Modifying shared state or unexpected variable shadowing def update_state(data=[]): data.append(1) return data
What Happens:
Default mutable argument accumulates values across separate function calls.
The Clean FixCorrected Code
def update_state(data=None): if data is None: data = [] data.append(1) return data
Recommended Practice:
Never use random for security-sensitive values — always reach for secrets.token_urlsafe() or secrets.token_hex().
Why It Happens

Default arguments in Python are evaluated once at function definition time, not at invocation time.

!Trap 3: Resource Leaks & High-Scale Performance Regressions
Architecture Trap
The Bug (Avoid)Buggy Code
# Opening resources in a tight loop without closing or bounding memory files = [open(f"log_{i}.txt", "w") for i in range(5000)]
What Happens:
OSError: [Errno 24] Too many open files (file descriptor exhaustion)
The Clean FixCorrected Code
for i in range(5): with open(f"log_{i}.txt", "w") as f: f.write("Safe")
Recommended Practice:
Never use random for security-sensitive values — always reach for secrets.token_urlsafe() or secrets.token_hex().
Why It Happens

Failing to release system resources leads to process termination under heavy production workloads.

06 / Real-World Systems

Real-World Systems in Action

See how scripts, backend services, and high-throughput authentication systems all lean on the right randomness module at scale:

Developer ProductivityFoundation Script

Local Developer Automation Tool

SYSTEM #1
Organization Context

Individual developers and IT administrators automate everyday repetitive workflows.

Why This Pattern Works Here

A clean standalone script providing fast, local automation with zero dependencies.

Production Code Wrapper
Local Developer Automation Tool
import secrets
secure_token = secrets.token_hex(8)
print("Cryptographically secure session token:", secure_token)
Cloud MicroservicesScaled Service · Applied

Backend API Service Integration

SYSTEM #2
Organization Context

SaaS platforms run backend microservices processing requests concurrently.

Why This Pattern Works Here

A modular service handler integrating validation and structured responses.

Production Code Wrapper
Backend API Service Integration
# Service integration layer
def handle_request(payload: dict) -> dict:
    return {"status": "success", "processed": True}
Distributed SystemsEnterprise · Architecture

High-Throughput Cloud Architecture

SYSTEM #3
Organization Context

Authentication servers MUST use secrets (never random) for password resets, session tokens, and CSRF protection to prevent prediction attacks.

Why This Pattern Works Here

Authentication servers MUST use secrets (never random) for password resets, session tokens, and CSRF protection to prevent prediction attacks.

Production Code Wrapper
High-Throughput Cloud Architecture
# High-resilience production pattern
import time

def process_stream(events: list) -> int:
    processed = 0
    for event in events:
        if event:
            processed += 1
    return processed
Key Takeaways

Key Points & Concept Review

  • Never use the random module for security-sensitive tokens, passwords, or keys.
  • Use secrets (CSPRNG) for authentication tokens, API credentials, and cryptographic tasks.
  • random.seed() ensures deterministic, reproducible simulation runs in scientific code and tests.
  • random.choices() samples with replacement and supports weights; random.sample() samples without replacement.
  • random.shuffle() randomizes a list in place using the Fisher-Yates algorithm.
  • Always use secrets.compare_digest() to compare security tokens to prevent timing attacks.
Study Guide & References

Frequently Asked Questions & Reference Hub

FAQ & References
Growth Mindset: When in Doubt About Security, Reach for secrets

If a value will ever gate access — a password reset link, a session ID, an API key — default to secrets. The Mersenne Twister behind random is fast and reproducible precisely because it's predictable, which is the opposite of what security needs.

Related Built-in References Right Here
PyLabs Library Docs
Ready for Action

You've Mastered Randomness & Cryptographic Secrets!

You understand random.seed(), choices() vs sample(), shuffle(), secrets tokens, and compare_digest(). Now lock in your knowledge with hands-on practice.

Hands-on Code Challenge

Seed the PRNG, pick a weighted color, and format the deterministic result exactly.

+40 XP Reward

Earn experience points toward your developer rank as soon as you pass.

~3 Minutes to Complete

Bite-sized knowledge check with instant explanations for every question.

Worked example

Pick a random item deterministically using a seed.

Example
import random
random.seed(1)
print(random.choice(['apple', 'pear']))

How it works, step by step

  1. random.choice picks an element; seed(1) makes it repeatable.

Your notes

Sign in to keep private notes alongside this lesson.

Sign in to take notes

Random Numbers & Cryptographic Secrets practice

Sign-in required

Set random.seed(10). Use random.choice(['red', 'green', 'blue']) to pick a color, and random.randint(1, 100) to pick a number. Print: Picked {color} with score {score}.

Coding Practice is Locked

Sign in to access the interactive Python editor, write and run code checks against automated tests, use hints, and earn XP.

Practice quiz

Check yourself

15 questions with explanations after submission, so you can correct course as you go.

Random Numbers & Cryptographic Secrets quiz

Locked

You must be signed in to take this quiz. Sign in to test your understanding, track your answers, and earn XP.

Lesson incomplete

Not completed

Create an ID or sign in to save lesson completion across devices.

Create ID to save
Module lesson progress0/6 complete
Module cheat sheet