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

    Programador pensando enquanto analisa código em dois monitores
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Learn Programming from Scratch: Beginner Guide

    Learn programming from scratch with a practical roadmap covering logic, languages, tools, exercises, projects, debugging, Git, and consistent study habits.

    Ler mais

    Tempo de leitura: 6 minutos
    10/07/2026
    ícone de loop com o texto 'For' abaixo
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python for Loops: Complete Beginner Guide

    Learn Python for loops with sequences, range, enumerate, zip, dictionaries, nested loops, break, continue, comprehensions, and practical examples.

    Ler mais

    Tempo de leitura: 4 minutos
    10/07/2026
    Manipulação e formatação de strings em Python
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python Strings: Complete Beginner Guide

    Learn Python strings with creation, indexing, slicing, methods, formatting, Unicode, searching, validation, splitting, joining, and practical examples.

    Ler mais

    Tempo de leitura: 4 minutos
    10/07/2026
    código Python de uma tupla com o texto separado formando a frase "O que são tuplas?"
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python Tuples: Complete Beginner Guide

    Learn Python tuples with creation, packing, unpacking, indexing, immutability, methods, named records, function returns, and practical examples.

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    símbolo de PI
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python Floats: Complete Beginner Guide

    Learn Python floats with decimal values, arithmetic, conversion, rounding, precision limits, comparisons, Decimal, math functions, and practical examples.

    Ler mais

    Tempo de leitura: 4 minutos
    10/07/2026
    notebook mostrando tela a teclado
    Fundamentals
    Foto de perfil de Leandro Hirt da Academify

    Python Input and Output: Complete Guide

    Learn Python input and output with input(), print(), conversions, validation, formatting, files, command-line arguments, and practical interactive programs.

    Ler mais

    Tempo de leitura: 4 minutos
    10/07/2026