Django is a "batteries included" framework for security too: CSRF, XSS escaping, ORM parameters, the clickjacking header and password hashing come built in. But in real projects holes usually appear in the parts you write: production settings, permission checks, password policy and attempt limits. This lesson is a practical checklist built around the OWASP Top 10. CSRF, XSS and SQL injection themselves are covered in a separate lesson.

⚠️ A checklist is a reminder, not a scanner: security depends on code review as well as on settings.

Built-in protections

RiskBuilt-in protectionWhat switches it off
CSRFCsrfViewMiddleware, {% csrf_token %}@csrf_exempt
XSStemplate auto-escapingsafe filter, mark_safe
SQL injectionORM parametersraw()/extra() + f-string
ClickjackingX-Frame-Options: DENY@xframe_options_exempt
Passwordshash + salt (PBKDF2)storing a plain password
SessionHttpOnly, SameSite=Lax cookieweakening the settings
Layers a request passes
Layers a request passes

A request passes several layers, but which user may access which object is something only you check, inside the view. That is also the most common OWASP risk: Broken Access Control.

manage.py check --deploy and SECURE_*

check --deploy audits settings.py for production. Each finding comes with a security.Wxxx code and --fail-level WARNING turns a warning into a CI failure. Run it with the production settings: with DEBUG = True the result is misleading.

bash
python manage.py check --deploy
python manage.py check --deploy --fail-level WARNING
text
System check identified some issues:

WARNINGS:
?: (security.W004) SECURE_HSTS_SECONDS ...
?: (security.W008) SECURE_SSL_REDIRECT ...
?: (security.W012) SESSION_COOKIE_SECURE ...
?: (security.W016) CSRF_COOKIE_SECURE ...
?: (security.W018) You should not have DEBUG set to True in deployment.

A typical fix:

python
import os