- Protecciones nativas frente a XSS, CSRF y SQLi con opciones de endurecimiento adicional.
- Autenticación, permisos y control de acceso en vistas y plantillas totalmente integrados.
- Plantillas de login/logout y restablecimiento de contraseña listas para personalizar.
- Gestión proactiva de vulnerabilidades como CVE-2025-26699 mediante actualizaciones.

Si vienes de frameworks minimalistas como Flask y te preocupan los detalles finos de seguridad, es normal que te preguntes hasta dónde llega Django. Este framework no solo trae autenticación y ORM; su filosofía es adelantarse a los problemas más habituales del desarrollo web y darte mecanismos para mitigarlos sin pelearte con lo básico en cada proyecto.
La idea de que “Django lo hace todo por ti” es tentadora, pero conviene mirar bajo el capó. Aunque incorpora defensas por defecto contra XSS, CSRF y SQL Injection, la seguridad es un proceso continuo: exige configurar, revisar y probar. También hay casos avanzados (limitación de velocidad, cabeceras CSP, endurecimiento de cookies) que no vienen activados de serie y que debes completar tú.
Qué hace Django por ti desde el minuto uno
De fábrica, Django aplica una serie de salvaguardas que cubren buena parte del OWASP Top 10. El motor de plantillas escapa las variables por defecto para frenar XSS reflejado, el middleware de CSRF añade y valida tokens en formularios, y el ORM genera consultas parametrizadas para evitar inyecciones SQL. Además, incorpora cabeceras contra clickjacking y un sistema robusto de sesiones.
También ofrece una configuración segura inicial: ALLOWED_HOSTS limita los dominios válidos, se pueden activar cookies de sesión seguras y HTTPOnly, y es trivial forzar HTTPS. Sin embargo, el despliegue real exige pulir detalles: redirecciones a HTTPS, CSP, aislamiento de red, rotación de claves, y políticas de contraseñas adecuadas para tu contexto.
En cuanto a autenticación y autorización, el framework trae modelos de usuarios y grupos, permisos por modelo y utilidades en vistas y plantillas para controlar el acceso. Esta integración estrecha entre autenticación, permisos y vistas te ayuda a definir quién puede ver o hacer qué, sin reinventar la rueda.
Por último, si te preguntas por funcionalidades como la limitación de velocidad, Django no la incluye de serie. Puedes implementarla con middleware o a nivel de servidor (por ejemplo, Nginx/Traefik/HAProxy) o con apps y librerías especializadas; la clave es ubicar el control lo más cerca posible del borde de red.

Defensas contra XSS: plantillas que escapan y buenas prácticas
El Cross-Site Scripting se cuela cuando contenido controlado por el usuario termina ejecutándose como script en el navegador. Django ayuda escapando cualquier variable que pintes en plantillas con el motor por defecto, así los caracteres peligrosos no se interpretan como HTML/JS.
¿Cuándo podrías exponerte? Si desactivas el escape con filtros como safe o usas mark_safe() sin sanitizar. Úsalos solo si realmente confías en la fuente o has pasado el HTML por un saneador. Evita construir atributos HTML, URLs o bloques de script concatenando cadenas; usa las herramientas de plantilla y el contexto correcto.
Refuerza la protección con cabeceras y políticas del navegador: Content Security Policy (CSP) para restringir orígenes de scripts, X-XSS-Protection (en navegadores que aún lo usan) y evitar inline scripts cuando sea posible. Combinar el escape del template con CSP reduce muchísimo la superficie de XSS.
CSRF: tokens, middleware y formularios
La falsificación de petición en sitios cruzados empuja a un usuario autenticado a lanzar acciones sin su consentimiento. El middleware de Django introduce un token CSRF único y las vistas validan que el POST venga desde un formulario legítimo.
En tus plantillas, no olvides incluir {% csrf_token %} dentro de los formularios. Para peticiones AJAX, añade el token como cabecera (por ejemplo, X-CSRFToken) o payload según tu librería de frontend. Si trabajas con dominios cruzados, revisa CSRF_TRUSTED_ORIGINS y la directiva SameSite de cookies.
Si expones endpoints JSON con sesión, valora doble capa: CSRF y comprobación de origen. Y recuerda que los endpoints sin sesión (p. ej., JWT puro) no usan CSRF de la misma forma; allí la defensa es distinta (tokens portadores, rotación, etc.).
Inyección SQL: el ORM como barrera y sus excepciones
El ORM de Django compone consultas usando parámetros, no concatenación de strings. Cuando filtras con .filter() o construyes QuerySets, la capa traduce a SQL seguro. Esto reduce enormemente la probabilidad de SQLi en el acceso normal a datos.
El riesgo aparece cuando sales del camino feliz: consultas crudas con .raw() o .extra(), plantillas SQL manuales o conversión insegura de parámetros. Si necesitas SQL nativo, parametriza siempre y, si puedes, encapsula el acceso en funciones auditables.
Otra defensa útil es el principio de mínimo privilegio: al usuario de base de datos dale solo permisos imprescindibles. Limitar operaciones desde la propia BD mitiga impactos si alguna consulta acaba comprometida.
Autenticación, permisos y control de acceso fino
Django incluye un sistema de usuarios, grupos y permisos desde el primer minuto. Los permisos básicos add/change/delete se crean automáticamente para cada modelo, y puedes definir permisos propios en class Meta de tus modelos (por ejemplo, uno específico como can_mark_returned para bibliotecarios).
En vistas basadas en función, añade @login_required para obligar a iniciar sesión; en clases, LoginRequiredMixin o PermissionRequiredMixin te evitan boilerplate. Ajusta login_url y redirect_field_name para controlar flujos y redirecciones.
Desde plantillas, comprueba {{ user.is_authenticated }} y usa {{ perms }} para consultar permisos, por ejemplo {{ perms.catalog.can_mark_returned }}. Esto permite ocultar enlaces o acciones cuando el usuario no tiene privilegios.
Si ya tienes experiencia con autenticación externa, Django es flexible: backends de autenticación pluggables para LDAP, SSO o proveedores sociales, además de autenticación basada en sesión, tokens y alternativas como JWT mediante librerías.
Contraseñas: almacenamiento, validación y rotación
Las contraseñas se almacenan con hashing seguro: por defecto PBKDF2 con sal, con soporte para Argon2 y otros esquemas. El framework gestiona la actualización progresiva del algoritmo; cuando un usuario inicia sesión con un hash antiguo, puedes rehash en caliente.
Activa validadores de contraseña para exigir longitud, complejidad y denegar credenciales comunes. Django incluye validadores de uso habitual y puedes añadir uno que consulte listas de contraseñas comprometidas. No expongas pistas de si un email existe o no en el sistema más allá de lo imprescindible.
El flujo de restablecimiento de contraseña está listo para usar: formularios para pedir email, envío del enlace y confirmación. Solo se envían correos a direcciones ya registradas, así evitas filtrar usuarios. Configura el backend de correo y personaliza las plantillas para tu marca.
En producción es imprescindible servir todo por TLS. Activa SECURE_SSL_REDIRECT y marca cookies como Secure y HttpOnly para sesiones y CSRF. Ajusta SameSite según necesites interoperar con iframes o subdominios.
La caducidad de sesión y la protección contra secuestro de cookies son básicas: limita SESSION_COOKIE_AGE, considera renovación de sesiones tras login, y si manejas información sensible, invalida sesiones tras periodos de inactividad. Evalúa almacenar sesiones en caché o BD según tu arquitectura de programación backend.
Páginas de login/logout y restablecimiento: URLs y plantillas
Django trae URLs y vistas prediseñadas para autenticación. Incluye django.contrib.auth.urls bajo /accounts/ y crea tus plantillas en templates/registration/: login.html, logged_out.html, y las cinco de restablecimiento (password_reset_form, password_reset_done, password_reset_email, password_reset_confirm, password_reset_complete).
Si no defines LOGIN_REDIRECT_URL, tras iniciar sesión se intentará ir a /accounts/profile/. Configura la redirección a tu home u otra vista. Personaliza la plantilla de logout para evitar que te lleve a la pantalla del Admin; así mantienes coherencia de navegación para todos los usuarios.
Asegúrate de que el directorio de plantillas se añade a TEMPLATES en settings.py. Esto evita el clásico error de plantilla no encontrada cuando pruebas /accounts/login/ por primera vez.
Control de acceso en interfaces y vistas
En plantillas, muestra enlaces de Login/Logout condicionalmente: {{ user.is_authenticated }} para decidir qué enseñar. Cuando generes URLs de inicio y cierre de sesión, añade ?next={{ request.path }} para que el usuario vuelva a la página desde la que inició el flujo.
En vistas de función, @login_required gestiona redirecciones automáticas. En clases, LoginRequiredMixin y PermissionRequiredMixin te dan el mismo comportamiento con menos código. Alternativamente, restringe por rol de staff con staff_member_required si procede.
Ejemplo práctico: listar “mis préstamos”
Una casuística recurrente es mostrar al usuario su contenido. Imagina un catálogo de biblioteca: añades borrower = ForeignKey(User) a BookInstance para ligar cada ejemplar al prestatario y creas una propiedad is_overdue que compare due_back con la fecha actual.
En el Admin, incluye borrower en list_display y fieldsets para gestionar préstamos de forma rápida. Asigna estados como “On loan” y fechas futuras y pasadas para probar atrasos.
Crea una vista basada en clases que herede de LoginRequiredMixin y filtra con get_queryset() por borrower=self.request.user y estado “o” (en préstamo). Ordena por due_back para priorizar lo urgente. Usa una plantilla, por ejemplo bookinstance_list_borrowed_user.html, que marque en otro color cuando bookinst.is_overdue sea cierto.
Expón la ruta, por ejemplo /catalog/mybooks/, y añade un enlace en la barra lateral que solo se muestre si hay sesión iniciada. Si un usuario anónimo accede, será redirigido a la pantalla de login y, tras autenticarse, volverá a su lista de préstamos gracias al parámetro next.
Avisos rápidos: recursos, descripción, solución, detalle
Muchos equipos documentan hallazgos de seguridad con un esquema breve. Un formato útil agrupa “Recursos Afectados”, “Descripción”, “Solución” y “Detalle” para ir directo al grano y no perder trazabilidad.
- Recursos afectados
- Componentes, endpoints, modelos o versiones impactados por el riesgo o la mejora de seguridad.
- Descripción
- Resumen claro del problema o de la capacidad defensiva relevante en Django y su alcance.
- Solución
- Acciones concretas: parches, cambios de configuración, migraciones de datos, cabeceras a activar y pruebas a realizar.
- Detalle
- Evidencia técnica, rutas reproducibles, referencias a documentación o CVE, y cualquier nota de despliegue.
Cookies, consentimiento y privacidad
Más allá de la técnica, hay obligaciones legales y de experiencia de usuario. Si usas analítica o publicidad personalizada, debes informar y permitir configurar o deshabilitar cookies, idealmente con un banner y un centro de preferencias. Esto incluye diferenciar cookies necesarias de las de marketing o analítica.
En tu texto de consentimiento, explica que se emplean cookies propias y de terceros con fines analíticos y publicitarios, que se pueden ajustar desde un enlace tipo “Configurar cookies” y que el perfilado se basa en hábitos de navegación (por ejemplo, páginas consultadas). Implementa la lógica para no cargar etiquetas hasta que haya consentimiento.
Aprendizaje con aplicaciones vulnerables: PyGoat y OWASP
Para practicar seguridad en Django con riesgo controlado, puedes usar proyectos educativos como PyGoat. Es una app intencionalmente vulnerable que te permite experimentar, localizar fallos típicos del OWASP Top 10 y, lo más importante, practicar cómo corregirlos con herramientas del framework.
Trabajar sobre PyGoat ayuda a entender dónde fallan los patrones comunes y a conectar mitigaciones reales de Django (escape de plantillas, tokens CSRF, permisos) con vulnerabilidades concretas. Es un buen entrenamiento para no relajarte con las protecciones por defecto.
Vulnerabilidades reales y parches: CVE-2025-26699
Incluso los frameworks maduros necesitan actualizaciones de seguridad periódicas. Se ha publicado CVE-2025-26699 (CVSS 5.0), una vulnerabilidad que puede provocar denegación de servicio cuando se procesan cadenas extremadamente largas mediante django.utils.text.wrap() o el filtro de plantilla wordwrap.
Versiones afectadas: 4.2 hasta 4.2.20, 5.0 anteriores a 5.0.13 y 5.1 anteriores a 5.1.7. La recomendación es clara: aplica las releases de seguridad publicadas por Django. Si en tus vistas o plantillas tratas textos muy grandes de usuarios, revisa dónde utilizas estos helpers y considera límites defensivos de tamaño.
Consulta las referencias oficiales: security releases de Django y ficha CVE. Automatiza la vigilancia con dependabot/renovate y auditorías de SCA para detectar versiones vulnerables cuanto antes.
Hardening adicional y despliegue
Más allá de las defensas integradas, conviene reforzar la capa de perímetro. Aplica limitación de velocidad a nivel de proxy (Nginx, Traefik, HAProxy o un WAF) para frenar fuerza bruta y scrapers agresivos. Complementa con bloqueo progresivo y reCAPTCHA solo donde aporte valor.
Activa cabeceras de seguridad: CSP, X-Frame-Options, Referrer-Policy, Strict-Transport-Security y X-Content-Type-Options. Ajusta CSP gradualmente en modo report-only al principio para evitar romper funcionalidades.
Protege secretos y entornos: gestiona SECRET_KEY y credenciales en un vault, segmenta redes y bases de datos, usa roles mínimos, y habilita auditoría. Valida subidas de ficheros (tipo, tamaño y antivirus) y evita servirlos directamente si son sensibles.
Observabilidad y respuesta: registra eventos de autenticación, cambios críticos y errores inesperados. Integra alertas y trazabilidad con herramientas como Sentry, Prometheus/Grafana o el stack de logs que uses. Ensaya procedimientos de revocación de sesiones y rotación de claves.
Para integraciones externas, revisa SSRF y timeouts: restringe destinos de requests salientes, establece límites de tamaño y de tiempo, y valida URLs de usuario con listas de permitidos cuando sea necesario.
La seguridad en Django no es magia negra: combina buenas decisiones de diseño, configuraciones sensatas y mantenimiento continuo. Con las protecciones nativas (XSS, CSRF, SQLi), el sistema de permisos y las utilidades de autenticación, más un poco de hardening en el perímetro, puedes levantar aplicaciones sólidas que respeten la privacidad, resistan ataques comunes y se mantengan al día con parches como el de la CVE-2025-26699.
Tabla de Contenidos
- Qué hace Django por ti desde el minuto uno
- Defensas contra XSS: plantillas que escapan y buenas prácticas
- CSRF: tokens, middleware y formularios
- Inyección SQL: el ORM como barrera y sus excepciones
- Autenticación, permisos y control de acceso fino
- Contraseñas: almacenamiento, validación y rotación
- HTTPS, cookies y sesiones seguras
- Páginas de login/logout y restablecimiento: URLs y plantillas
- Control de acceso en interfaces y vistas
- Ejemplo práctico: listar “mis préstamos”
- Avisos rápidos: recursos, descripción, solución, detalle
- Cookies, consentimiento y privacidad
- Aprendizaje con aplicaciones vulnerables: PyGoat y OWASP
- Vulnerabilidades reales y parches: CVE-2025-26699
- Hardening adicional y despliegue
