El módulo secrets de Python genera valores aleatorios adecuados para contraseñas, tokens de recuperación, identificadores temporales y otros datos relacionados con seguridad. Aunque el módulo random es excelente para juegos y simulaciones, sus resultados son predecibles si un atacante conoce suficiente información sobre el estado interno del generador.
Cuando la imprevisibilidad forma parte de la seguridad, debes utilizar una fuente criptográficamente segura. En esta guía aprenderás a crear enteros, elegir elementos, generar tokens, construir contraseñas, comparar secretos y evitar errores frecuentes.
Random y secrets no resuelven el mismo problema
El módulo random utiliza un generador pseudoaleatorio diseñado para velocidad y reproducibilidad. Esto resulta útil cuando necesitas repetir una simulación con la misma semilla. No resulta apropiado para claves, enlaces privados o códigos de autenticación.
El módulo secrets utiliza la fuente de aleatoriedad proporcionada por el sistema operativo. La documentación oficial de secrets recomienda este módulo para contraseñas, autenticación y tokens de seguridad.
Para comprender el uso no criptográfico, consulta la guía del módulo random en Python.
Generar un entero seguro con randbelow()
import secrets
number = secrets.randbelow(100)
print(number)randbelow(100) devuelve un entero desde 0 hasta 99. El límite superior queda excluido. Para simular un dado del 1 al 6:
dice = secrets.randbelow(6) + 1
print(dice)Aunque este ejemplo funciona, un juego normal no necesita el coste adicional de una fuente criptográfica. Reserva secrets para escenarios donde la predicción tenga consecuencias.
Crear bits aleatorios con randbits()
import secrets
value = secrets.randbits(128)
print(value)
print(value.bit_length())randbits(k) genera un entero con hasta k bits aleatorios. Puede servir como base para identificadores internos, pero las funciones token_hex() y token_urlsafe() suelen ser más cómodas para transmitir valores.
Elegir un elemento de forma segura
import secrets
alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
character = secrets.choice(alphabet)
print(character)choice() selecciona un elemento de una secuencia no vacía. Puedes repetir la operación para construir un código:
code = "".join(
secrets.choice(alphabet)
for _ in range(8)
)
print(code)La guía de strings en Python explica join(), validación y manipulación de texto.
Generar tokens binarios, hexadecimales y seguros para URL
import secrets
raw_token = secrets.token_bytes(32)
hex_token = secrets.token_hex(32)
url_token = secrets.token_urlsafe(32)
print(raw_token)
print(hex_token)
print(url_token)token_bytes()devuelve bytes sin codificar.token_hex()devuelve texto hexadecimal.token_urlsafe()devuelve texto compatible con URLs y formularios.
El argumento representa la cantidad de bytes de entropía, no la longitud final exacta del texto. Un token hexadecimal utiliza dos caracteres por byte; uno codificado para URL suele ser aproximadamente un tercio más largo.
Crear un enlace de recuperación
import secrets
from urllib.parse import urlencode
def create_reset_url(base_url):
token = secrets.token_urlsafe(32)
query = urlencode({"token": token})
return token, f"{base_url}?{query}"
token, url = create_reset_url(
"https://example.com/reset-password"
)
print(url)La aplicación debe guardar una representación protegida del token, asociarla a un usuario y definir una expiración. No basta con generar un valor aleatorio; también debes controlar almacenamiento, uso único, revocación y tiempo de vida.
Generar una contraseña segura
import secrets
import string
def generate_password(length=20):
if length < 12:
raise ValueError(
"La longitud mínima es 12"
)
alphabet = (
string.ascii_letters
+ string.digits
+ "!@#$%&*+-_"
)
return "".join(
secrets.choice(alphabet)
for _ in range(length)
)
print(generate_password())Una contraseña aleatoria larga suele ser más resistente que una corta con reglas complicadas. La guía del generador seguro de contraseñas en Python desarrolla un proyecto completo con validación y personalización.
Garantizar requisitos en la contraseña
Cuando una plataforma exige tipos específicos de caracteres, genera hasta cumplir todas las condiciones:
import secrets
import string
def generate_compliant_password(length=20):
alphabet = (
string.ascii_letters
+ string.digits
+ "!@#$%&*+-_"
)
while True:
password = "".join(
secrets.choice(alphabet)
for _ in range(length)
)
if not any(c.islower() for c in password):
continue
if not any(c.isupper() for c in password):
continue
if not any(c.isdigit() for c in password):
continue
if not any(c in "!@#$%&*+-_" for c in password):
continue
return passwordNo registres la contraseña generada. La guía de logging en Python explica cómo evitar información sensible en archivos de registro.
Comparar tokens con compare_digest()
import secrets
stored = "a4c8f2d1"
provided = "a4c8f2d1"
if secrets.compare_digest(stored, provided):
print("Token válido")
else:
print("Token inválido")compare_digest() reduce la exposición a ataques basados en diferencias de tiempo durante la comparación. Ambos valores deben ser del mismo tipo: bytes con bytes o strings ASCII con strings compatibles.
No confundas tokens con cifrado
Un token aleatorio no cifra información. secrets puede generar material aleatorio para bibliotecas criptográficas, pero no implementa por sí mismo cifrado, hashing de contraseñas o firmas digitales.
La guía de seguridad de OWASP sobre almacenamiento de contraseñas recomienda algoritmos especializados y configuraciones resistentes. Nunca guardes contraseñas en texto plano ni intentes inventar un sistema criptográfico propio.
Validar longitud y configuración
def validate_token_size(size):
if not isinstance(size, int):
raise TypeError("El tamaño debe ser entero")
if size < 16:
raise ValueError(
"Utiliza al menos 16 bytes"
)
return sizeLa cantidad adecuada depende del riesgo y del tiempo de vida del token. Para tokens sensibles y duraderos, 32 bytes es una elección común. Evita tamaños pequeños solamente para obtener URLs más cortas.
Ejemplo: códigos de invitación únicos
import secrets
def create_invitation_code(existing_codes):
while True:
code = secrets.token_urlsafe(12)
if code not in existing_codes:
existing_codes.add(code)
return code
used = set()
for _ in range(5):
print(create_invitation_code(used))La comprobación evita una colisión dentro del conjunto actual. La guía de sets en Python explica por qué la pertenencia es eficiente en esta estructura.
Manejo de errores
Las funciones de secrets suelen ser sencillas, pero el código que almacena o entrega tokens puede fallar. Captura excepciones específicas en la capa correspondiente:
try:
token = secrets.token_urlsafe(32)
save_token(token)
except DatabaseError as error:
logger.exception(
"No fue posible guardar el token"
)
raiseLa guía de try y except en Python explica cómo conservar el traceback y evitar bloques demasiado amplios.
Errores frecuentes
- Usar
randompara contraseñas o recuperación de cuentas. - Crear tokens demasiado cortos.
- Registrar tokens, claves o contraseñas.
- Reutilizar un token después de su primer uso.
- No establecer una expiración.
- Guardar contraseñas en lugar de hashes seguros.
- Confundir aleatoriedad con cifrado.
- Comparar secretos sensibles con lógica casera.
Conclusión
Utiliza secrets cuando un valor impredecible proteja una cuenta, sesión o recurso. Domina randbelow(), choice(), token_hex(), token_urlsafe() y compare_digest(). Después completa la solución con expiración, almacenamiento seguro, uso único y registros que nunca expongan información confidencial.






