Instalación y configuración de passlib
Cuando desarrollamos aplicaciones web que manejan usuarios, uno de los aspectos más críticos es la gestión segura de contraseñas. Nunca debemos almacenar contraseñas en texto plano en nuestra base de datos, ya que esto representa un riesgo de seguridad enorme si alguien accede a nuestros datos.
La solución es utilizar funciones hash que transforman las contraseñas en cadenas irreversibles. Para esto, necesitamos una biblioteca especializada que implemente algoritmos de hash seguros y actualizados.
¿Qué es passlib?
Passlib es una biblioteca de Python diseñada específicamente para el manejo seguro de contraseñas. Proporciona una interfaz unificada para trabajar con múltiples algoritmos de hash y se encarga automáticamente de los aspectos técnicos de seguridad que necesitamos.
Esta biblioteca es especialmente útil porque:
- Simplifica el proceso de hash y verificación de contraseñas
- Maneja automáticamente la generación de valores únicos para cada contraseña
- Utiliza algoritmos modernos y seguros por defecto
- Se integra perfectamente con FastAPI y otras aplicaciones web
Instalación de passlib
Para instalar passlib en nuestro proyecto FastAPI, utilizamos pip con las dependencias necesarias para el algoritmo de hash que vamos a emplear:
pip install "passlib[bcrypt]"
El parámetro [bcrypt]
instala las dependencias adicionales necesarias para utilizar el algoritmo bcrypt, que es uno de los más recomendados para el hash de contraseñas.
Si trabajas con un archivo requirements.txt
, añade esta línea:
passlib[bcrypt]==1.7.4
Configuración básica
Una vez instalada la biblioteca, necesitamos configurar el contexto de hash en nuestro proyecto FastAPI. Crea un archivo llamado security.py
donde centralizaremos toda la lógica de seguridad:
from passlib.context import CryptContext
# Configuración del contexto de hash
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
El objeto CryptContext
es el núcleo de passlib. Los parámetros que utilizamos son:
schemes=["bcrypt"]
: Especifica que utilizaremos el algoritmo bcryptdeprecated="auto"
: Permite que passlib maneje automáticamente las actualizaciones de algoritmos
Estructura del proyecto
Para mantener nuestro código organizado, la estructura recomendada sería:
mi_proyecto/
├── main.py
├── security.py
├── models.py
└── requirements.txt
En el archivo security.py
mantendremos todas las funciones relacionadas con la seguridad, mientras que main.py
contendrá nuestras rutas de FastAPI.
Verificación de la instalación
Para confirmar que passlib está correctamente instalado y configurado, podemos crear un pequeño script de prueba:
from passlib.context import CryptContext
# Crear el contexto
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# Probar la configuración
test_password = "mi_contraseña_secreta"
hashed = pwd_context.hash(test_password)
print(f"Contraseña original: {test_password}")
print(f"Hash generado: {hashed}")
print(f"Verificación exitosa: {pwd_context.verify(test_password, hashed)}")
Si ejecutas este código y obtienes una salida similar a esta, la instalación es correcta:
Contraseña original: mi_contraseña_secreta
Hash generado: $2b$12$xyz...
Verificación exitosa: True
El hash generado será diferente cada vez que ejecutes el código, lo cual es exactamente el comportamiento que buscamos para mantener la seguridad.
¿Te está gustando esta lección?
Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.
Más de 25.000 desarrolladores ya confían en CertiDevs
Hash y verificación de contraseñas
Ahora que tenemos passlib configurado, vamos a implementar las funciones fundamentales para el manejo seguro de contraseñas: crear hashes y verificar que una contraseña coincide con su hash almacenado.
Creación de funciones de utilidad
En nuestro archivo security.py
, añadiremos dos funciones esenciales que utilizaremos en toda nuestra aplicación:
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def hash_password(password: str) -> str:
"""
Genera un hash seguro de la contraseña proporcionada
"""
return pwd_context.hash(password)
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""
Verifica si una contraseña en texto plano coincide con su hash
"""
return pwd_context.verify(plain_password, hashed_password)
La función hash_password()
toma una contraseña en texto plano y devuelve su versión hasheada que podemos almacenar de forma segura. La función verify_password()
compara una contraseña introducida por el usuario con el hash almacenado, devolviendo True
si coinciden.
Integración con FastAPI
Vamos a crear un ejemplo práctico de cómo utilizar estas funciones en una aplicación FastAPI. Primero, definimos un modelo para el registro de usuarios:
from pydantic import BaseModel
class UserCreate(BaseModel):
username: str
password: str
class User(BaseModel):
id: int
username: str
hashed_password: str
Ahora implementamos las rutas para registro y verificación de usuarios:
from fastapi import FastAPI, HTTPException
from security import hash_password, verify_password
app = FastAPI()
# Simulamos una base de datos en memoria
fake_users_db = {}
user_id_counter = 1
@app.post("/register")
async def register_user(user: UserCreate):
global user_id_counter
# Verificar si el usuario ya existe
if user.username in fake_users_db:
raise HTTPException(status_code=400, detail="El usuario ya existe")
# Hashear la contraseña antes de almacenarla
hashed_password = hash_password(user.password)
# Crear el usuario en la "base de datos"
fake_users_db[user.username] = {
"id": user_id_counter,
"username": user.username,
"hashed_password": hashed_password
}
user_id_counter += 1
return {"message": "Usuario registrado exitosamente"}
Verificación de credenciales
Para implementar un sistema de login básico, creamos una función que verifique las credenciales del usuario:
class UserLogin(BaseModel):
username: str
password: str
def authenticate_user(username: str, password: str):
"""
Autentica un usuario verificando sus credenciales
"""
user = fake_users_db.get(username)
if not user:
return False
if not verify_password(password, user["hashed_password"]):
return False
return user
@app.post("/login")
async def login_user(user_login: UserLogin):
user = authenticate_user(user_login.username, user_login.password)
if not user:
raise HTTPException(
status_code=401,
detail="Credenciales incorrectas"
)
return {"message": f"Bienvenido, {user['username']}!"}
Ejemplo práctico completo
Aquí tienes un ejemplo completo que puedes probar para entender el flujo completo de registro y autenticación:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from security import hash_password, verify_password
app = FastAPI()
class UserCreate(BaseModel):
username: str
password: str
class UserLogin(BaseModel):
username: str
password: str
# Base de datos simulada
fake_users_db = {}
@app.post("/register")
async def register_user(user: UserCreate):
if user.username in fake_users_db:
raise HTTPException(status_code=400, detail="El usuario ya existe")
# El hash se genera automáticamente con valores únicos
hashed_password = hash_password(user.password)
fake_users_db[user.username] = {
"username": user.username,
"hashed_password": hashed_password
}
return {"message": "Usuario registrado exitosamente"}
@app.post("/login")
async def login_user(user_login: UserLogin):
stored_user = fake_users_db.get(user_login.username)
if not stored_user:
raise HTTPException(status_code=401, detail="Usuario no encontrado")
# Verificamos la contraseña contra el hash almacenado
if not verify_password(user_login.password, stored_user["hashed_password"]):
raise HTTPException(status_code=401, detail="Contraseña incorrecta")
return {"message": f"Login exitoso para {user_login.username}"}
Consideraciones importantes
Cada vez que ejecutes hash_password()
con la misma contraseña, obtendrás un hash diferente. Esto es normal y deseable para la seguridad, ya que cada hash incluye valores únicos que previenen ataques de diccionario.
# Ejemplo de comportamiento normal
password = "mi_contraseña"
hash1 = hash_password(password)
hash2 = hash_password(password)
print(hash1) # $2b$12$abc123...
print(hash2) # $2b$12$def456...
print(hash1 == hash2) # False - ¡Esto es correcto!
# Pero ambos se verifican correctamente
print(verify_password(password, hash1)) # True
print(verify_password(password, hash2)) # True
La función verify_password()
es la única forma segura de comprobar si una contraseña es correcta. Nunca intentes comparar hashes directamente, ya que cada hash es único incluso para la misma contraseña.
Aprendizajes de esta lección
- Comprender la importancia de no almacenar contraseñas en texto plano.
- Instalar y configurar passlib con el algoritmo bcrypt.
- Implementar funciones para generar y verificar hashes de contraseñas.
- Integrar la gestión segura de contraseñas en una aplicación FastAPI.
- Entender el comportamiento de los hashes únicos y la verificación segura de contraseñas.
Completa FastAPI y certifícate
Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.
Asistente IA
Resuelve dudas al instante
Ejercicios
Practica con proyectos reales
Certificados
Valida tus conocimientos
Más de 25.000 desarrolladores ya se han certificado con CertiDevs