Django security
47 Claude skills for security tradecraft across AppSec, Pentest, Blue Team, and GRC. NL/EU regulatory-anchored. Pattern-level discipline.
npx -y skills add roodlicht/accans-sec-skills --skill django-securityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Django security review — CSRF, ORM-level SQL injection (raw/extra/annotate), template injection via |safe, admin hardening, middleware ordering, settings deploy checklist, and recent Django CVE patterns.
SKILL.md
11.1 KB, as published. Nobody here has run it
Django Security
When to use
This skill is the Django-specific layer on top of secure-coding. Use it when Django gets in its own way: defaults that invite misunderstanding, ORM paths that look safe but still allow SQLi, or settings that may go wrong per environment.
Triggers on:
- A question like "review this Django app", "is our deploy safe", "what are our SECURE_ settings", "are we using CSRF correctly", "Django admin hardening".
- Presence of
manage.py,settings.py,urls.py,models.py,views.py, Django middleware class paths inMIDDLEWARE, DRFviewsets.pyorserializers.py. - A PR that introduces
@csrf_exempt,|safe,mark_safe,.raw(),.extra(),RawSQL, orHttpResponse(user_input). - Django version bumps that are a security release (release notes will explicitly mention fixes).
- A handoff from
security-reviewwhere Django is in the stack.
When NOT (handoff)
- General Python secure-coding (not Django-specific) →
secure-coding. - DRF as an API layer overlaps heavily with
api-security: OAuth/JWT flow, OWASP API Top 10 belong there. Django-specific DRF foot-guns (e.g.HyperlinkedModelSerializerauthorization bypass) stay here. - SAST tool config (Bandit, Semgrep
p/django) →sast-orchestrator. - Dep vulns in Django or third-party packages →
cve-triage. - Deploy infrastructure (nginx, gunicorn, containers) →
container-hardening/iac-security. - Secrets handling (SECRET_KEY, DB creds) →
secrets-scanner.
Approach
Six phases. Django defaults are reasonably safe; most issues arise where you deliberately override them.
1. Settings and deploy checklist
python manage.py check --deploy is a Django-built-in checker. Always run it, fix every warning before production. Not sufficient, but necessary.
Critical settings:
DEBUG = Falsein production.DEBUG = Trueleaks stack traces, env vars, SQL queries: full app-internal exposure. No exceptions.ALLOWED_HOSTSexplicit. Wildcard['*']in prod is a Host-header-injection vector.SECRET_KEYfrom environment or vault, never in source. Rotation schedule on suspicion of leak. Seesecrets-scanner.SECURE_SSL_REDIRECT = True,SECURE_HSTS_SECONDS >= 31536000withSECURE_HSTS_INCLUDE_SUBDOMAINSandSECURE_HSTS_PRELOAD,SESSION_COOKIE_SECURE = True,CSRF_COOKIE_SECURE = True. Only where TLS is guaranteed.SESSION_COOKIE_HTTPONLY = True(default),SESSION_COOKIE_SAMESITE = 'Lax'or'Strict'.SECURE_CONTENT_TYPE_NOSNIFF = True,SECURE_REFERRER_POLICYexplicitly set.CSRF_TRUSTED_ORIGINSwith full scheme+host where relevant (Django 4+).PASSWORD_HASHERSwith Argon2 first, then PBKDF2 as fallback. Requiresdjango-argon2package.- Database:
ATOMIC_REQUESTSwhere appropriate, separate DB user with least privilege (no DROP/CREATE rights for the web user).
2. ORM and SQL injection
Django's ORM parameterizes by default. SQLi arises in places where you step out of it:
.raw(): string formatting in the raw queryset gives SQLi. Use placeholder params:Model.objects.raw('SELECT * FROM app_model WHERE name = %s', [name]). Notf'SELECT ... WHERE name = {name}'..extra(): deprecated, still widely used.where=,params=,select=can be abused. Prefer to replace withFunc(),RawSQL()with params, orannotate()withExpressionWrapper.RawSQL: parameter list MUST be used. The example in the Django docs showsRawSQL("select col from sometable where othercol = %s", (someparam,)). Follow that.QuerySet.annotate()/aggregate()with dict keys from user input. Historically vulnerable (see CVE-2022-28346, SQL injection via column aliases in dict keys). Reviewer rule: dict keys in annotate/aggregate never come from user input, hardcode them.- Order-by with user input.
.order_by(request.GET.get('sort'))lets users pick columns. Allowlist permitted column names. __in=with large lists from user input: not SQLi but still DoS. Cap list size.
Django CVE references (within a 3-year window, verify against release notes):
- CVE-2022-28346: QuerySet.annotate/aggregate SQLi via dict keys. Fixed in Django 2.2.28, 3.2.13, 4.0.4. Canonical example of "the ORM is safe, except when ..."
- CVE-2023-43665: Truncator DoS via crafted HTML. Fixed in 3.2.22, 4.1.12, 4.2.6.
- More recent CVEs in the Django 4.2/5.x series:
[verify against https://docs.djangoproject.com/en/dev/releases/security/]for the current window.
3. Template injection and XSS
Django templates have autoescape on by default. XSS arises when you turn it off.
{{ user_input|safe }}: renders raw HTML. Apply only to content you have controlled yourself (e.g. sanitized HTML from a trusted bleach call), never on unwashed user input.{% autoescape off %}: disables escaping for the entire block. Rarely needed, review every use.mark_safe(s)in Python code: same effect as|safe. Ifscontains user input or is composed of user input, you have XSS.format_html('<a href="{}">', user_url): URL attributes are a separate problem.javascript:URLs viahrefbypass HTML escaping. Validateuser_url.startswith(('http:', 'https:')).- Server-side template injection: if you render templates yourself with user-controlled template strings (
Template(user_input).render(...)), you get SSTI with potential RCE. Never do this. HttpResponse(user_input): bypasses template autoescape because there is no template. Userender()or encode explicitly.
4. CSRF model
Django's CsrfViewMiddleware is on by default for POST/PUT/PATCH/DELETE.
@csrf_exempt: turns CSRF off for a specific view. Use only on endpoints where CSRF structurally cannot work (e.g. webhook receivers with signature verification). Every@csrf_exemptin a PR is a security-review moment.- DRF and CSRF: DRF
SessionAuthenticationenforces CSRF,TokenAuthentication/JWTdoes not (stateless). Mixed auth modes: be explicit about which endpoints use which model. CSRF_COOKIE_HTTPONLY: defaultFalse, which is needed for JS to read the token. Do not change unless you have a custom CSRF setup.CSRF_TRUSTED_ORIGINS: stricter interpretation since Django 4 (full origin with scheme). Without correct config: legitimate POSTs are 403-rejected.
5. Auth, session, and admin
django.contrib.auth.password_validation: never remove. Length check, common-password check, attribute-similarity check must be active.AUTHENTICATION_BACKENDS: custom backends are a classic foot-gun. Every custom backend must be timing-safe (identical response time for "user does not exist" vs. "wrong password").- Admin interface:
ADMIN_URLnot/admin/(security through obscurity + bot-traffic reduction).- IP allowlist via middleware or reverse proxy for
/admin/*. - 2FA required via
django-otpordjango-allauthwith TOTP. is_staffandis_superuserassigned carefully. Superuser status only for a small team.
- Session management:
SESSION_COOKIE_AGEreasonably short,SESSION_EXPIRE_AT_BROWSER_CLOSEwhere appropriate, session rotation on privilege change (automatic on login since Django 2+). - django-allauth/django-axes for rate-limiting on login (brute-force prevention). Native Django has no rate limiter on auth endpoints.
6. Misc and verification-loop
- File uploads:
FileFielddoes not validate content type.UploadedFile.content_typeis set by the client. Validate magic bytes, whitelist extensions, store outside web root. - Open redirects:
redirect(request.GET.get('next'))without validation is an open redirect. Django hasurl_has_allowed_host_and_scheme(url, allowed_hosts=...)for this purpose. - Middleware order:
SecurityMiddlewarebelongs high,CsrfViewMiddlewarebeforeAuthenticationMiddleware, custom middleware with side effects close to the view. Wrong order can produce subtle bypasses. - django-admin
checkwith--deployrequired in CI. - Bandit + Semgrep
p/djangoas the SAST layer, seesast-orchestrator.
Verification-loop: Layer 1 scope (settings file + urls + middleware stack all walked through?), assumptions (autoescape on everywhere, no |safe on user input?), gaps (DRF views included in the same review?). Layer 2 specifically on CVE IDs (verify against Django release notes, not memory), security-setting names (Django sometimes changes default values per major version), admin-hardening claims backed by concrete middleware/package.
Output
Follow the security-review report format, with a Django-specific scan summary at the top:
Django security review — <app/project>
Django version: <x.y.z> (latest security release: <...>)
Deploy checklist (python manage.py check --deploy):
Warnings: N → resolved/open
DEBUG in prod: <False | FINDING>
ALLOWED_HOSTS: <explicit | wildcard — FINDING>
SECURE_* settings: <complete | gaps>
PASSWORD_HASHERS: <Argon2 first | PBKDF2 default>
Code patterns:
@csrf_exempt: <N, location, reason>
|safe / mark_safe: <N, on user input? FINDING>
.raw() / .extra(): <N, parameterized?>
order_by(user input): <list>
Admin:
URL not /admin/: <yes/no>
IP restriction: <yes/no>
2FA on staff: <yes/no>
Version check:
On latest security release: <yes | N months behind>
CVE relevance: <handoff to cve-triage>
Findings (severity-sorted, follow security-review format)
Verification-loop: ...
References
- Django Security — https://docs.djangoproject.com/en/stable/topics/security/. Official security topic page.
- Django Deployment Checklist — https://docs.djangoproject.com/en/stable/howto/deployment/checklist/. Exactly what
manage.py check --deploychecks. - Django Security Releases — https://docs.djangoproject.com/en/dev/releases/security/. Canonical CVE log per version.
- Django Admin Security — https://docs.djangoproject.com/en/stable/ref/contrib/admin/#adminsite-objects. Admin configuration reference.
- OWASP Django Security Cheat Sheet — https://cheatsheetseries.owasp.org/cheatsheets/Django_Security_Cheat_Sheet.html.
- Django Rest Framework — Authentication — https://www.django-rest-framework.org/api-guide/authentication/. DRF auth-mode matrix.
- django-axes — https://django-axes.readthedocs.io/. Login-attempt rate-limiting.
- django-otp — https://django-otp-official.readthedocs.io/. 2FA on admin.
Categories
- appsec