Fix cards for Python and Django

A01:2025 Broken Access Control

  • Cross-site request forgery (CSRF)CWE-352

    Keep django.middleware.csrf.CsrfViewMiddleware in MIDDLEWARE and do not exempt views: it checks the Origin (or, over HTTPS, the Referer) against the site and CSRF_TRUSTED_ORIGINS, and the token.

  • Missing object-level authorization (IDOR and BOLA)CWE-862 · CWE-639 · CWE-863

    Build one queryset that holds the rule, from request.user and its tenant and role, never from request data: Invoice.objects.filter(tenant=member.tenant), narrowed to owner=user for members.

  • Open redirectCWE-601

    Prefer Django's LoginView, which checks next itself; add other hosts with success_url_allowed_hosts.

  • Path traversalCWE-22 · CWE-23 · CWE-36 · CWE-59

    Resolve both paths and compare whole components: base = Path(settings.FILES_ROOT).resolve(), target = (base / name).resolve(), and return FileResponse(target.open('rb')) only if target.is_relative_to(base). resolve() collapses ../ and follows symlinks; the check also catches absolute names.

  • Server-side request forgery (SSRF)CWE-918

    Attach the policy to the connection, not the URL string. Allow only http/https and ports 80/443 and reject credentials; resolve the host (or read an IP literal in any notation) and reject loopback, private, link-local and IPv4-mapped addresses with ipaddress; connect to the vetted IP; re-validate every redirect; cap time and bytes.

A04:2025 Cryptographic Failures

  • Weak password hashingCWE-916 · CWE-759 · CWE-327

    Install argon2-cffi and list django.contrib.auth.hashers.Argon2PasswordHasher first in PASSWORD_HASHERS, keeping older hashers after it.

A05:2025 Injection

  • Cross-site scripting (XSS)CWE-79

    Let Django's autoescaping work: render {{ value }} without |safe, mark_safe() or {% autoescape off %}, so < > ' " and & become entities in text and quoted attributes.

  • CSV formula injectionCWE-1236

    User text in a CSV is attacker-controlled. If a cell starts with = + - @, a tab, CR, LF or NUL, prefix it with an apostrophe so a spreadsheet treats it as text, then write it with csv.writer, which escapes commas, quotes and newlines.

  • SQL injectionCWE-89

    Let the ORM build the query: Order.objects.filter(customer__name=name, status=status) sends both values as bound parameters.

A06:2025 Insecure Design

  • ClickjackingCWE-1021

    Keep Django's XFrameOptionsMiddleware (X-Frame-Options defaults to DENY) and add a CSP with ContentSecurityPolicyMiddleware and SECURE_CSP = {"frame-ancestors": [CSP.NONE]}, which browsers honor over X-Frame-Options.

  • Unrestricted file uploadCWE-434 · CWE-646 · CWE-73 · CWE-770

    Decide the type from the bytes and never let the client pick the name.

A07:2025 Authentication Failures

A08:2025 Software or Data Integrity Failures

  • Mass assignment (over-posting)CWE-915

    Give every ModelForm an explicit allowlist, fields = ["display_name", "bio"]: it binds only those fields and ignores the rest of the request.

Not covered in Django yet

These weaknesses have fix cards in other stacks but not in this one.

Supported versions

Each feature release gets mainstream support until the next one and security fixes for about eight months after; LTS releases get security fixes for three years. Django 2028 and later each get three years. Django support policy.

Django
VersionLatestStatusReleasedEnd of support
6.16.1.2SupportedMainstream support until April 2027; the cards use it.5 August 2026December 2027
6.06.0.9Security fixes only3 December 2025April 2027
5.2 (LTS)5.2.18Security fixes only2 April 2025April 2028
CPython
VersionLatestStatusReleasedEnd of support
3.153.15.0Supported9 October 2026October 2031
3.143.14.8Supported7 October 2025October 2030
3.133.13.16Security fixes only7 October 2024October 2029
3.123.12.15Security fixes only2 October 2023October 2028

Django 6.1 needs Python 3.12 or later. CPython release schedule

How the fix cards choose versions

Minimum secure versions

The lowest release in each supported line that fixes every published advisory listed here. Anything older is missing a security fix.

PackageAt leastWhy
django6.1.2, 6.0.9 or 5.2.18The 6 October 2026 security releases: CVE-2026-84429 (quadratic header parsing), CVE-2026-77050 (language-code denial of service), CVE-2026-87890 (request forgery through spatial lookup bytes) and CVE-2026-87975 (privilege abuse in model formsets with editable primary keys). Advisory
CPython3.15.0, 3.14.8, 3.13.16 or 3.12.15Six CVEs fixed on 30 September and 1 October 2026, such as an ssl use-after-free (CVE-2026-19445), a tarfile filter bypass (CVE-2026-19672) and unbounded zipfile decompression (CVE-2026-15310); 3.11 to 3.13 also fix two more tarfile CVEs, and the fix for one of them, CVE-2026-87910, was in no 3.14 release on 11 October 2026. Advisory

Middleware and security order

  1. SecurityMiddleware: Near the top of MIDDLEWARE, so an HTTPS redirect happens before other middleware runs.
  2. SessionMiddleware: Before any middleware that uses the session; after UpdateCacheMiddleware if you cache.
  3. CommonMiddleware: Then CsrfViewMiddleware, before any view middleware that assumes CSRF attacks have been dealt with.
  4. AuthenticationMiddleware: After SessionMiddleware, which it uses for request.user; then MessageMiddleware.
  5. XFrameOptionsMiddleware and ContentSecurityPolicyMiddleware: Frame protection, and CSP from the SECURE_CSP setting (new in Django 6.0).
  6. Views: login_required or LoginRequiredMiddleware, plus querysets filtered by the owner.

Secure defaults and settings

SettingValueWhy
DEBUGFalse (but startproject sets True)Never True in production.
SESSION_COOKIE_SECURE, CSRF_COOKIE_SECUREFalseSet True.
SECURE_HSTS_SECONDS, SECURE_SSL_REDIRECT0, FalseSet HSTS to at least a year once HTTPS works everywhere.
SECURE_CONTENT_TYPE_NOSNIFF, X_FRAME_OPTIONS, SECURE_REFERRER_POLICY, SECURE_CROSS_ORIGIN_OPENER_POLICYTrue, 'DENY', 'same-origin', 'same-origin'Keep them.
SESSION_COOKIE_SAMESITE'Lax'Keep it.
PASSWORD_HASHERSPBKDF2-SHA256 first, 1,500,000 iterationsPut Argon2PasswordHasher first if argon2-cffi is installed; old hashes upgrade at sign-in.
SECURE_CSPEmptySet a policy and add ContentSecurityPolicyMiddleware.

Escape hatches to search for

Framework APIs that switch a protection off. Each one is sometimes needed; search for them in review and make each use justify itself.

APIRiskInstead
mark_safe(), |safe, {% autoescape off %}Turns off escaping.format_html() for markup around escaped values.
@csrf_exemptRemoves CSRF protection from a view.Keep CsrfViewMiddleware and set CSRF_TRUSTED_ORIGINS for real cross-origin needs.
raw(), extra(), RawSQL, cursor.execute() with formattingSQL injection.The ORM, or parameters passed separately.
ALLOWED_HOSTS = ['*']Host header poisoning in generated links such as password-reset emails.Your host names.
ModelForm with fields = '__all__'Mass assignment of fields added later.An explicit fields list.
HttpResponseRedirect(request.GET['next'])Open redirect.url_has_allowed_host_and_scheme() with require_https.

Checklist, mapped to ASVS 5.0.0

  • v5.0.0-1.2.1 Templates rely on autoescaping; no request data reaches mark_safe or |safe.
  • v5.0.0-3.5.1 CsrfViewMiddleware is on and no cookie-authenticated view is @csrf_exempt.
  • v5.0.0-8.2.2 Views load objects through querysets filtered by request.user.
  • v5.0.0-1.2.4 No SQL is built with string formatting.
  • v5.0.0-15.3.3 Forms list their fields explicitly.
  • v5.0.0-3.4.1 SECURE_HSTS_SECONDS is at least 31536000.
  • v5.0.0-3.3.1 Session and CSRF cookies are Secure.
  • v5.0.0-13.4.2 DEBUG is off and manage.py check --deploy reports no warnings you have not accepted.
  • v5.0.0-11.4.2 Passwords use Django's hashers with Argon2 or PBKDF2 at the current iteration count.

References

  1. Django: middleware ordering
  2. Django: deployment checklist
  3. Django: security in Django
  4. Django: settings reference
  5. Django: download and supported versions
  6. Django security releases issued: 6.1.2, 6.0.9 and 5.2.18

All fix cards