Un generador de contraseñas es un proyecto útil para practicar strings, bucles, funciones, entrada de terminal y validación. También es una herramienta de seguridad, así que una decisión importa desde el principio: las contraseñas deben utilizar una fuente de aleatoriedad criptográficamente segura. El módulo random sirve para juegos y simulaciones, no para secretos.
En este tutorial construirás un generador de contraseñas seguro en Python con los módulos estándar secrets y string. Añadirás longitud configurable, reglas de caracteres, validación, una interfaz de terminal y pruebas. El programa genera las contraseñas localmente y no las guarda.
Por qué utilizar secrets
random es determinista y puede reproducir secuencias a partir de su estado interno. Esto es útil para pruebas, pero peligroso para contraseñas, tokens, enlaces de recuperación y claves de API. secrets utiliza la fuente segura del sistema operativo.
Generador seguro mínimo
import secrets
import string
alfabeto = (
string.ascii_letters
+ string.digits
+ string.punctuation
)
contrasena = "".join(
secrets.choice(alfabeto)
for _ in range(20)
)
print(contrasena)La guía de strings en Python explica unión, búsqueda y otros métodos utilizados para construir el resultado.
Crear una función reutilizable
import secrets
import string
def generar_contrasena(longitud=20):
if longitud < 12:
raise ValueError(
"La longitud mínima es 12"
)
alfabeto = (
string.ascii_letters
+ string.digits
+ string.punctuation
)
return "".join(
secrets.choice(alfabeto)
for _ in range(longitud)
)La función devuelve la contraseña en lugar de imprimirla. Así puede utilizarse desde una CLI, una interfaz gráfica o una aplicación web. Consulta la guía de funciones en Python para repasar parámetros y valores de retorno.
Garantizar categorías obligatorias
Algunos servicios exigen al menos una minúscula, una mayúscula, un dígito y un símbolo. Una estrategia segura es generar una candidata completa, validarla y repetir solamente cuando no cumple la política.
import secrets
import string
def cumple_politica(contrasena):
return (
any(c.islower() for c in contrasena)
and any(c.isupper() for c in contrasena)
and any(c.isdigit() for c in contrasena)
and any(c in string.punctuation for c in contrasena)
)
def generar_contrasena(longitud=20):
if longitud < 12:
raise ValueError(
"La longitud mínima es 12"
)
alfabeto = (
string.ascii_letters
+ string.digits
+ string.punctuation
)
while True:
candidata = "".join(
secrets.choice(alfabeto)
for _ in range(longitud)
)
if cumple_politica(candidata):
return candidataEl operador utilizado para buscar símbolos se explica en la guía del operador in en Python.
Elegir grupos de caracteres
import string
def crear_alfabeto(
*,
minusculas=True,
mayusculas=True,
digitos=True,
simbolos=True,
):
partes = []
if minusculas:
partes.append(string.ascii_lowercase)
if mayusculas:
partes.append(string.ascii_uppercase)
if digitos:
partes.append(string.digits)
if simbolos:
partes.append(string.punctuation)
alfabeto = "".join(partes)
if not alfabeto:
raise ValueError(
"Selecciona al menos un grupo"
)
return alfabetoLos argumentos con nombre hacen que las llamadas sean más claras. Si reduces el tamaño del alfabeto, compensa utilizando una contraseña más larga.
Excluir caracteres ambiguos
Al escribir manualmente una contraseña, caracteres como cero y O mayúscula, o uno, l minúscula e I mayúscula, pueden confundirse.
AMBIGUOS = set("0O1lI")
def eliminar_ambiguos(alfabeto):
return "".join(
caracter
for caracter in alfabeto
if caracter not in AMBIGUOS
)Versión completa
import secrets
import string
AMBIGUOS = set("0O1lI")
def generar_contrasena(
longitud=20,
*,
simbolos=True,
evitar_ambiguos=False,
):
if longitud < 12:
raise ValueError(
"La longitud mínima es 12"
)
grupos = [
string.ascii_lowercase,
string.ascii_uppercase,
string.digits,
]
if simbolos:
grupos.append(string.punctuation)
alfabeto = "".join(grupos)
if evitar_ambiguos:
alfabeto = "".join(
c for c in alfabeto
if c not in AMBIGUOS
)
while True:
candidata = "".join(
secrets.choice(alfabeto)
for _ in range(longitud)
)
requisitos = [
any(c.islower() for c in candidata),
any(c.isupper() for c in candidata),
any(c.isdigit() for c in candidata),
]
if simbolos:
requisitos.append(
any(
c in string.punctuation
for c in candidata
)
)
if all(requisitos):
return candidataAñadir una interfaz de terminal
import argparse
def crear_parser():
parser = argparse.ArgumentParser(
description=(
"Genera una contraseña aleatoria segura."
)
)
parser.add_argument(
"--longitud",
type=int,
default=20,
help="Longitud mínima 12",
)
parser.add_argument(
"--sin-simbolos",
action="store_true",
help="Excluye signos de puntuación",
)
parser.add_argument(
"--evitar-ambiguos",
action="store_true",
help="Excluye 0, O, 1, l e I",
)
return parser
def main():
args = crear_parser().parse_args()
contrasena = generar_contrasena(
args.longitud,
simbolos=not args.sin_simbolos,
evitar_ambiguos=args.evitar_ambiguos,
)
print(contrasena)
if __name__ == "__main__":
main()La guía para crear una CLI con argparse explica opciones, banderas, validación y empaquetado.
Crear passphrases
Una passphrase combina palabras elegidas aleatoriamente. Puede ser más fácil de escribir, pero la seguridad depende del tamaño de la lista, la cantidad de palabras y la selección independiente.
import secrets
PALABRAS_DEMO = [
"ancla", "bambu", "cobre", "delfin",
"estrella", "bosque", "galaxia", "puerto",
"isla", "jardin", "kernel", "linterna",
]
def passphrase_demo(cantidad=5):
if cantidad < 4:
raise ValueError(
"Utiliza al menos cuatro palabras"
)
return "-".join(
secrets.choice(PALABRAS_DEMO)
for _ in range(cantidad)
)Esta lista pequeña es solamente una demostración de programación. Una herramienta real necesita una lista amplia y revisada.
Generar no es almacenar
Un sistema de autenticación no debe guardar contraseñas recuperables en texto plano, logs o archivos de configuración. Debe almacenar un hash creado por un algoritmo específico para contraseñas y una sal única. No diseñes tu propio sistema criptográfico.
Probar las reglas
import string
def test_contrasena_predeterminada():
contrasena = generar_contrasena()
assert len(contrasena) == 20
assert any(c.islower() for c in contrasena)
assert any(c.isupper() for c in contrasena)
assert any(c.isdigit() for c in contrasena)
assert any(
c in string.punctuation
for c in contrasena
)Las pruebas deben verificar reglas, no predecir una salida aleatoria. Consulta la guía de Pytest para principiantes.
Utilizar un entorno virtual
Este proyecto usa solamente la biblioteca estándar, pero un entorno aislado sigue siendo útil cuando añades una interfaz o herramientas de prueba. La guía de entornos virtuales en Python explica el flujo recomendado.
Errores frecuentes
- Usar
random.choice()para contraseñas o tokens. - Confiar en una contraseña corta solo porque contiene símbolos.
- Guardar o registrar automáticamente el resultado.
- Construir un patrón predecible con posiciones fijas.
- Usar una lista mínima para una passphrase real.
- Confundir generación con hashing seguro.
Conclusión
Un generador de contraseñas seguro en Python es pequeño porque la biblioteca estándar proporciona la fuente de aleatoriedad adecuada. Utiliza secrets, elige una longitud generosa, valida la política, evita guardar la salida y trata el hashing como una responsabilidad separada.
Documentación y seguridad
Consulta la documentación oficial de Python sobre el módulo secrets. Para el almacenamiento de credenciales, revisa la guía de almacenamiento de contraseñas de OWASP.




