Generate Secure Random Numbers in Python with secrets

Published on: June 1, 2026
Reading time: 4 minutes
Geração de números aleatórios seguros usando secrets em Python

Have you ever thought about how your computer decides which number comes next in a lottery? In programming, creating randomness is not as simple as it seems. If you are building a security system such as a Python password generator, using the wrong tool can leave the door open for hackers. This is where the secrets module comes in — the definitive solution for anyone who needs real cryptographic security. This article shows how to generate secure random numbers in Python with secrets and explains why it is superior to traditional methods.

Two types of randomness

In computing there are two main types of random numbers: pseudo-random and cryptographically secure. Most functions used day-to-day, like those in the Python random module, belong to the first group. They are excellent for simulations and games but have a critical weakness: they are based on deterministic algorithms. If someone discovers the initial value (called the seed), they can predict every subsequent number.

For protecting sensitive data you need the second type. According to Wikipedia, modern cryptography depends entirely on entropy sources that cannot be reproduced by attackers.

random vs secrets: key differences

Python’s own documentation warns that the random module should not be used for security or cryptography purposes — it was designed for statistical modeling and speed, not protection against attacks. The secrets module, introduced in Python 3.6, fills that gap. It uses randomness sources provided by the operating system (such as /dev/urandom on Unix or CryptGenRandom on Windows), which draw from hardware noise and unpredictable system events.

Generating secure integers

import secrets

# Secure random integer between 0 and 99
secure_number = secrets.randbelow(100)
print(f"Your secure number: {secure_number}")

# Random number with 16 bits of depth
secure_bits = secrets.randbits(16)
print(f"16-bit number: {secure_bits}")

Creating tokens for authentication and URLs

Often you need secure text sequences rather than numbers. The secrets module makes creating hexadecimal tokens or Base64-encoded strings straightforward — essential for password reset links or API keys.

import secrets

# Secure hex token (32 characters)
hex_token = secrets.token_hex(16)
print(f"Hex Token: {hex_token}")

# URL-safe token (Base64)
url_token = secrets.token_urlsafe(16)
print(f"URL Token: {url_token}")

Using token_urlsafe is strongly recommended because it generates characters that do not need manual encoding, avoiding errors such as UnicodeDecodeError.

Choosing elements securely

secrets.choice() works identically to the random version but with the security engine behind it. Use it whenever you need to pick from a list without predictable patterns.

Building a robust password generator

import string
import secrets

def generate_secure_password(length=16):
    """Generate a strong password using the secrets module."""
    characters = string.ascii_letters + string.digits + string.punctuation
    password = ''.join(secrets.choice(characters) for _ in range(length))
    return password

if __name__ == "__main__":
    new_password = generate_secure_password(20)
    print("Your new secure password:", new_password)

    # Generating a token for account recovery
    recovery_token = secrets.token_urlsafe(32)
    print("Security token:", recovery_token)

Best practices for sensitive data

Never display secrets in logs — avoid using the logging system to record tokens generated by secrets. For comparing a user-supplied token against a stored one, use secrets.compare_digest(a, b). This prevents timing attacks where hackers deduce the correct value by measuring server response times. Use at least 32 bytes of entropy for security tokens to ensure brute-force protection. Consulting OWASP recommendations is always a good idea for anyone wanting to go deeper into web application security.

Comparison: secrets vs random

The random module has no cryptographic security and is deterministic with a known seed, but is fast and ideal for simulations and games. The secrets module is cryptographically secure and non-deterministic, making it the right choice for passwords, tokens, and authentication keys — at a slightly higher computational cost that is imperceptible in most applications.

Frequently Asked Questions

Is secrets available in all Python versions?

It was introduced in Python 3.6. Earlier versions require updating the development environment.

Can I use secrets to create random file names?

Yes. Using secrets.token_hex() for upload file names is a great practice that prevents collisions and file enumeration attacks.

Why use secrets.compare_digest instead of ==?

This function compares two strings in constant time regardless of how similar they are, preventing hackers from deducing the secret by measuring server response time.

Does secrets completely replace random?

No. For statistical simulations, scientific experiments, or games, random is still preferable because it is faster. Use secrets only where unpredictability is a security requirement.

Can I use secrets to generate AES encryption keys?

Yes. secrets.token_bytes(32) is the ideal way to generate a 256-bit key for symmetric encryption algorithms.

How many characters should a secure password have?

Current recommendations suggest a minimum of 12 to 16 characters. Using secrets guarantees those characters are truly random.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Pattern matching com match case em Python
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python match-case: Pattern Matching Explained

    Learn how Python match-case works for structural pattern matching, including values, sequences, dictionaries, guards, and dataclasses with practical examples.

    Ler mais

    Tempo de leitura: 4 minutos
    30/05/2026
    Como resolver loop infinito que nunca termina em Python
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Why Does My Loop Never End? Find the Solution

    Find out why your Python loop never ends and learn how to fix infinite loops caused by missing increments, wrong

    Ler mais

    Tempo de leitura: 6 minutos
    29/05/2026
    Logo do Python com as palavras global e local
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python Variable Scope Explained Clearly

    Learn Python variable scope with clear examples of local, global, enclosing, built-in scope, LEGB rules, global, nonlocal, and common mistakes.

    Ler mais

    Tempo de leitura: 9 minutos
    28/05/2026
    Comparativo dos melhores cursos de Python para aprender programação
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    8 Best Python Courses for Beginners in 2026

    Compare the 8 best Python courses for beginners in 2026, including Academify, Alura, Udemy, Coursera, Codecademy, Harvard CS50, EBAC, and

    Ler mais

    Tempo de leitura: 7 minutos
    28/05/2026
    Como usar f-strings para formatar números e moedas em Python
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python f-strings: Format Numbers and Currency

    Learn how to use Python f-strings to format numbers, currency, percentages, alignment, and zero-padding with clean and practical examples.

    Ler mais

    Tempo de leitura: 6 minutos
    28/05/2026
    Uso do operador ternário em Python para condições rápidas
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python Ternary Operator: Clean One-Line If

    Learn how Python ternary operators work, when to use one-line if expressions, how to avoid nested logic, and how to

    Ler mais

    Tempo de leitura: 8 minutos
    21/05/2026