agentsclimarketplace

Django security

Skill roodlicht/accans-sec-skills/skills/django-security

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.From its SKILL.md

Install
npx -y skills add roodlicht/accans-sec-skills --skill django-security

Assembled 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.

SKILL.md

11.1 KB, ~2.8k tokens by cl100k_base, 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 in MIDDLEWARE, DRF viewsets.py or serializers.py.
  • A PR that introduces @csrf_exempt, |safe, mark_safe, .raw(), .extra(), RawSQL, or HttpResponse(user_input).
  • Django version bumps that are a security release (release notes will explicitly mention fixes).
  • A handoff from security-review where 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. HyperlinkedModelSerializer authorization 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 = False in production. DEBUG = True leaks stack traces, env vars, SQL queries: full app-internal exposure. No exceptions.
  • ALLOWED_HOSTS explicit. Wildcard ['*'] in prod is a Host-header-injection vector.
  • SECRET_KEY from environment or vault, never in source. Rotation schedule on suspicion of leak. See secrets-scanner.
  • SECURE_SSL_REDIRECT = True, SECURE_HSTS_SECONDS >= 31536000 with SECURE_HSTS_INCLUDE_SUBDOMAINS and SECURE_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_POLICY explicitly set.
  • CSRF_TRUSTED_ORIGINS with full scheme+host where relevant (Django 4+).
  • PASSWORD_HASHERS with Argon2 first, then PBKDF2 as fallback. Requires django-argon2 package.
  • Database: ATOMIC_REQUESTS where 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]). Not f'SELECT ... WHERE name = {name}'.
  • .extra(): deprecated, still widely used. where=, params=, select= can be abused. Prefer to replace with Func(), RawSQL() with params, or annotate() with ExpressionWrapper.
  • RawSQL: parameter list MUST be used. The example in the Django docs shows RawSQL("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. If s contains 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 via href bypass HTML escaping. Validate user_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. Use render() 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_exempt in a PR is a security-review moment.
  • DRF and CSRF: DRF SessionAuthentication enforces CSRF, TokenAuthentication/JWT does not (stateless). Mixed auth modes: be explicit about which endpoints use which model.
  • CSRF_COOKIE_HTTPONLY: default False, 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_URL not /admin/ (security through obscurity + bot-traffic reduction).
    • IP allowlist via middleware or reverse proxy for /admin/*.
    • 2FA required via django-otp or django-allauth with TOTP.
    • is_staff and is_superuser assigned carefully. Superuser status only for a small team.
  • Session management: SESSION_COOKIE_AGE reasonably short, SESSION_EXPIRE_AT_BROWSER_CLOSE where 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: FileField does not validate content type. UploadedFile.content_type is 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 has url_has_allowed_host_and_scheme(url, allowed_hosts=...) for this purpose.
  • Middleware order: SecurityMiddleware belongs high, CsrfViewMiddleware before AuthenticationMiddleware, custom middleware with side effects close to the view. Wrong order can produce subtle bypasses.
  • django-admin check with --deploy required in CI.
  • Bandit + Semgrep p/django as the SAST layer, see sast-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

Categories

  • appsec

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.