Oculta secretos en la configuración del logger, no en cada punto de llamada
Una sola línea — log.info(request) — y cada API key, token de sesión y contraseña de ese objeto de request acaba de quedar en texto plano en tus logs, en tu agregador de logs y a la vista de todos los que tienen acceso de lectura. La solución no es más disciplina. Es mover la redacción al único lugar por el que pasa toda línea de log: la configuración del logger.
Por qué falla la redacción en el punto de llamada
El patrón común es borrar los secretos donde se registran:
log.info("auth attempt", user=user.id, token="***REDACTED***")
Esto funciona hasta el día en que alguien registra el objeto completo en lugar de campos elegidos a mano, agrega un nuevo campo sensible al payload, o copia un log.info(request) de depuración desde una rama y olvida borrarlo. La redacción en el punto de llamada es una regla que cada ingeniero debe recordar en cada línea, para siempre. El modo de falla es un campo olvidado, y siempre hay alguien que olvida.
Redacta una vez, en el logger
Un processor (structlog) o un filter (la logging estándar) se ubica en la tubería por la que pasa todo evento. Inspecciona el evento, reemplaza las claves sensibles con un marcador y solo entonces entrega el registro al formatter y a los handlers. Nada llega al disco ni a tu agregador sin borrar.
import structlog
SENSITIVE = {"password", "token", "authorization", "api_key", "secret", "cookie"}
def redact(logger, method_name, event_dict):
for key in list(event_dict):
if key.lower() in SENSITIVE:
event_dict[key] = "***REDACTED***"
return event_dict
structlog.configure(processors=[redact, structlog.processors.JSONRenderer()])
Un processor de structlog es simplemente un callable que recibe (logger, method_name, event_dict) y devuelve el dict para el siguiente processor de la cadena. Coloca redact antes del renderer y cada evento queda borrado antes de serializarse.
La biblioteca estándar tiene la misma junta. Una subclase de logging.Filter muta el LogRecord antes de que cualquier handler lo emita:
import logging
class RedactFilter(logging.Filter):
def filter(self, record):
for key in SENSITIVE:
if hasattr(record, key):
setattr(record, key, "***REDACTED***")
record.msg = scrub(record.msg) # regex sobre el string del mensaje
return True # conserva el registro; solo lo mutamos
Adjunta el filter a tus handlers y se ejecuta en cada registro.
Reglas prácticas
| Decisión | Haz esto |
|---|---|
| Dónde redactar | En la config del logger (processor/filter), no en el punto de llamada |
| Qué buscar | Una denylist de nombres de clave más regex para valores (bearer tokens, keys) |
| Coincidencia de claves | Sin distinguir mayúsculas; normaliza Authorization y authorization |
| Si no hay coincidencia | Devuelve/conserva el registro — un filter que devuelve True muta, no descarta |
| Payloads anidados | Recorre dicts de forma recursiva; los secretos se esconden en headers, body, meta |
Movidas avanzadas
- Busca valores, no solo claves. Una denylist de nombres de clave no atrapa un bearer token que cae en un mensaje de texto libre. Agrega sustitución por regex (
re.sub) para formas de valor de alta señal —Bearer [A-Za-z0-9._-]+,sk-[A-Za-z0-9]+— para atrapar un valor filtrado incluso bajo una clave inocente. - Redacta de forma recursiva. Los objetos de request se anidan. Tu processor debe descender por dicts y listas para que
event["headers"]["authorization"]quede borrado, no solo las claves de primer nivel. - Falla en cerrado en la tubería. Si un processor de redacción lanza una excepción, no dejes pasar la línea de log en crudo. Envuelve el cuerpo para que un error degrade a un registro totalmente enmascarado, nunca a uno sin borrar.
- Trátalo como una compuerta de seguridad, no como un lujo. Agrega un test que registre un secreto conocido y verifique que nunca aparezca en la salida renderizada. Ese test es lo que hace verdadero el "no se puede olvidar".
Recursos
- structlog — cómo funcionan los processors
- Python
logging.Filter— referencia de la biblioteca estándar - Better Stack — registrar datos sensibles de forma segura
- OWASP Logging Cheat Sheet — datos a excluir
¿Construyes funciones de IA que registran mucho? Yeda AI diseña, audita y despliega sistemas LLM en producción — incluida la higiene de logging y secretos a su alrededor. Habla con nosotros · Lee el blog