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
| Risk | Built-in protection | What switches it off |
|---|---|---|
| CSRF | CsrfViewMiddleware, {% csrf_token %} | @csrf_exempt |
| XSS | template auto-escaping | safe filter, mark_safe |
| SQL injection | ORM parameters | raw()/extra() + f-string |
| Clickjacking | X-Frame-Options: DENY | @xframe_options_exempt |
| Passwords | hash + salt (PBKDF2) | storing a plain password |
| Session | HttpOnly, SameSite=Lax cookie | weakening the settings |
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.
python manage.py check --deploy
python manage.py check --deploy --fail-level WARNINGSystem 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:
import os