Los tokens de recuperación, confirmación e invitación deben ser impredecibles. El módulo secrets usa la fuente segura de aleatoriedad del sistema y debe reemplazar a random para material de autenticación.
Generar un token para URL
import hashlib
import secrets
from datetime import UTC, datetime, timedelta
token = secrets.token_urlsafe(32)
token_hash = hashlib.sha256(token.encode()).hexdigest()
expira_en = datetime.now(UTC) + timedelta(minutes=20)
Envía el token original una vez y guarda solo su hash, finalidad, usuario y expiración. Cuando regrese, calcula el hash y busca un registro válido y sin usar. Márcalo como usado en la misma transacción que el cambio protegido.
No registres tokens ni los envíes a analytics. Evita fugas de URL por referrer, exige HTTPS y limita intentos. La guía de seguridad de APIs reúne más controles.
secrets.compare_digest() reduce diferencias de tiempo en comparaciones directas. Elige una cantidad explícita de bytes según el riesgo. Los tokens no reemplazan hash de contraseñas, MFA, expiración ni revocación.
La documentación oficial de secrets, consultada el 22 de julio de 2026, describe token_bytes, token_hex, token_urlsafe y compare_digest. La seguridad depende del ciclo completo, no solo de una cadena aleatoria.
Por qué random no sirve
El módulo random produce secuencias pseudoaleatorias para simulación y muestreo. Su estado puede reconstruirse cuando un atacante conoce suficiente salida, y una semilla basada en la hora reduce el espacio de búsqueda. Por ello no sirve para recuperación de contraseña, claves de API, cookies de sesión, invitaciones ni nonces.
secrets delega la generación en la fuente criptográficamente segura del sistema operativo. Su API compacta evita decisiones frágiles sobre semillas y algoritmos. No protege todo el flujo: expiración, almacenamiento, transporte y autorización siguen siendo responsabilidad de la aplicación.
Elegir la representación
token_bytes(n) devuelve bytes para protocolos o procesamiento interno. token_hex(n) representa cada byte con dos caracteres hexadecimales, por lo que 32 bytes producen 64 caracteres. token_urlsafe(n) usa Base64 compatible con URL y genera aproximadamente 1,3 caracteres por byte.
El argumento indica bytes, no la longitud final del texto. Elígelo explícitamente y documenta el modelo de amenazas. Treinta y dos bytes ofrecen margen amplio para muchos tokens en línea de alto valor, aunque los requisitos regulatorios pueden exigir otra decisión.
valor_binario = secrets.token_bytes(32)
valor_hex = secrets.token_hex(32)
valor_url = secrets.token_urlsafe(32)
No recortes la salida para ajustarla a una columna corta, pues eliminarías entropía. Cambia el esquema y valida los límites de la URL, cabecera o campo que transportará el valor.
Diseñar el ciclo de recuperación
Responde de forma equivalente para cuentas existentes e inexistentes, reduciendo la enumeración. Si la cuenta existe, genera el token, calcula su resumen y registra finalidad, usuario, expiración y estado de uso. Envía el original una sola vez por el canal previsto.
Al confirmar, rechaza entradas excesivas o mal formadas antes de consultar. Calcula el mismo resumen y valida finalidad, usuario, expiración y ausencia de uso. Cambia la contraseña y marca el token como consumido en la misma transacción. Revoca sesiones cuando la política lo exija.
def resumen_token(valor: str) -> str:
return hashlib.sha256(valor.encode("utf-8")).hexdigest()
def ha_expirado(instante: datetime, ahora: datetime) -> bool:
return instante <= ahora
Un hash rápido puede resumir un token aleatorio de alta entropía porque no hay un secreto humano débil que adivinar. Las contraseñas requieren algoritmos específicos, lentos y configurables como Argon2, scrypt o bcrypt.
Evitar filtraciones durante el transporte
Usa HTTPS en todo el flujo. Los tokens en query strings pueden aparecer en historial, logs de proxy, monitorización y cabeceras de referencia. La página de confirmación puede cambiar el token por estado temporal en el servidor y redirigir a una URL limpia. Aplica una política de referrer restrictiva y evita recursos de terceros.
Nunca incluyas el valor en errores, métricas ni trazas. El enmascaramiento central ayuda, pero el código debe evitar registrarlo. Limita intentos por cuenta, origen y token sin crear una denegación de servicio sencilla contra la víctima. Las alertas deben registrar anomalías sin conservar credenciales.
Comparar valores y crear códigos cortos
secrets.compare_digest(a, b) reduce variaciones temporales relacionadas con el contenido al comparar valores del mismo tipo. Es útil para MAC, resúmenes y secretos cortos disponibles en la aplicación. La comparación constante no corrige diferencias anteriores, como mensajes que revelan si una cuenta existe.
Cuando un canal exige un código numérico, secrets.randbelow() evita sesgos de transformaciones improvisadas:
codigo = f"{secrets.randbelow(1_000_000):06d}"
Un código de seis dígitos tiene solo un millón de posibilidades. Necesita vida breve, límite estricto de intentos y vínculo con usuario y finalidad. secrets.choice() selecciona caracteres con seguridad, pero el espacio resultante aún debe ser suficiente.
Probar sin debilitar producción
Las pruebas no deben esperar una salida exacta de secrets. Comprueba formato, longitud aproximada y ciclo de vida. Inyecta el reloj para probar expiración. Si una prueba necesita un valor conocido, sustituye el generador en el límite de la aplicación; nunca introduzcas una semilla predecible en producción.
Prueba también la concurrencia. Dos confirmaciones simultáneas no pueden consumir el mismo token, por lo que la base necesita una actualización atómica o restricción. Cubre valores expirados, finalidad o usuario incorrectos, entrada excesiva y registros ya consumidos.
Lista antes de producción
Documenta quién puede emitir, validar y revocar cada tipo de token. Finalidades diferentes deben usar registros o espacios distinguibles para impedir que una invitación se acepte como recuperación de contraseña. Confirma que la validación comprueba usuario y finalidad, no solo la existencia de un resumen.
Usa instantes del servidor con zona horaria. El cliente nunca debe elegir la expiración. Elimina registros vencidos mediante una tarea controlada, conservando solamente lo requerido por la política y la investigación de incidentes.
Inspecciona todos los caminos de observabilidad: servidor web, proxy, proveedor de correo, monitorización de errores y analytics. El token no debe aparecer en ninguno. Revisa cabeceras de caché, redirecciones y páginas de error. Finalmente, documenta acciones ante una filtración: revocación masiva, comunicación, rotación de credenciales relacionadas y evidencias mínimas para investigar. La generación segura solo es útil si operaciones puede contener un fallo.
Repite la revisión cuando cambien el canal de entrega, proveedor o formato de URL. Un cambio operativo pequeño puede crear un punto de filtración. Confirma que copias de seguridad y réplicas protegen los resúmenes según su sensibilidad y política de retención.
Mantén trazabilidad de la emisión sin registrar el token. Campos útiles incluyen hora, tipo, referencia interna de usuario, expiración y resultado. Restringe el acceso a esos registros y evita que la auditoría se convierta en otra fuente de datos personales.