Safeguard
Vulnerability Analysis

CVE-2018-14574: Open redirect in Django CommonMiddleware

CVE-2018-14574 let attackers abuse Django CommonMiddleware's APPEND_SLASH redirect to send users to external, attacker-controlled domains.

Vikram Iyer
Security Researcher
7 min read

CVE-2018-14574 is an open redirect vulnerability in django.middleware.common.CommonMiddleware, the middleware Django uses to normalize incoming request paths. When the APPEND_SLASH setting is enabled (the Django default) and a request arrives for a URL that doesn't resolve to a view and doesn't end in a trailing slash, CommonMiddleware issues a redirect to the same path with a slash appended. Before this vulnerability was fixed, Django built that redirect target directly from attacker-controlled request data without confirming the resulting URL still pointed at the application's own host. An attacker could craft a link that looked like it pointed at a trusted Django site but silently redirected the visitor's browser to an attacker-controlled domain — the textbook definition of an open redirect (CWE-601). The practical impact is phishing and credential-harvesting: users trust the initial domain in the link, click through, and land on a lookalike login page or malware download hosted elsewhere.

Open redirects are frequently dismissed as low-severity noise, but in a framework as widely deployed as Django they take on outsized importance. CommonMiddleware is enabled by default in virtually every Django project, which means any application that hadn't explicitly disabled APPEND_SLASH or patched Django was potentially exposed without any custom code being at fault — this was a framework-level default, not a misconfiguration by application developers.

Affected versions and components

  • Component: django.middleware.common.CommonMiddleware, triggered when APPEND_SLASH = True (Django's default) and PREPEND_WWW handling combine to build a redirect from request-supplied host/path data.
  • Affected releases: Django 1.11.x before 1.11.15, and Django 2.0.x before 2.0.8.
  • Fixed releases: Django 1.11.15 and Django 2.0.8, which added validation to ensure the constructed redirect target stays within the application's own host rather than trusting attacker-influenced path data.

If your project pinned Django to the 1.11 LTS line or the 2.0 release line and hadn't taken the August 2018 security update, it was affected regardless of application code — the vulnerability lived entirely in framework middleware that ships enabled by default.

CVSS, EPSS, and KEV context

NVD classifies CVE-2018-14574 as Medium severity under CVSS v3, consistent with its open-redirect classification: exploitation requires no privileges and low attack complexity, but it also requires user interaction (a victim has to click the crafted link) and the direct technical impact is limited to a redirect rather than direct compromise of confidentiality, integrity, or availability on the vulnerable server itself. The real-world risk is concentrated in the secondary effects — phishing, session/token leakage to attacker infrastructure via referrer or query-string leakage, and brand abuse — which CVSS's technical scoring model doesn't fully capture.

This CVE is not listed in CISA's Known Exploited Vulnerabilities (KEV) catalog, and there is no public evidence of widespread active exploitation campaigns targeting it specifically. Its EPSS (Exploit Prediction Scoring System) score has historically sat in the low range typical of open-redirect issues that require social engineering to weaponize, rather than the high scores associated with remote code execution or unauthenticated data-exposure bugs. That said, low EPSS does not mean zero risk: open redirects are commonly chained into phishing kits and OAuth/SSO flow abuse, where the trusted domain in the URL is the entire point of the attack.

Timeline

  • Report and fix development: The Django security team investigated a report that CommonMiddleware's slash-appending redirect logic could be manipulated to send users to an external domain, and developed a fix that validates the redirect target against the application's own host.
  • August 1, 2018 — Coordinated disclosure and patch release: The Django project publicly disclosed the issue and shipped Django 1.11.15 and 2.0.8 as security releases addressing the open redirect, alongside the corresponding CVE assignment (CVE-2018-14574).
  • Post-disclosure: Downstream Linux distributions and package maintainers (Debian, Red Hat, Ubuntu, and various Python package indexes) picked up and republished the advisory, and it was cataloged in NVD and other vulnerability databases shortly after the Django announcement.

Because this was handled through Django's standard coordinated security-release process, there was no extended window of public zero-day exposure between disclosure and patch availability — both were announced simultaneously, which is Django's standard practice for security fixes.

Remediation

  1. Upgrade Django. The direct fix is to update to Django 1.11.15 or later (for the 1.11 LTS line) or Django 2.0.8 or later (for the 2.0 line). Any Django release after these versions includes the corrected host-validation logic in CommonMiddleware. If you're running a much older or newer unsupported line today, move to a currently supported Django release rather than just the minimum patched version — later releases carry additional hardening against redirect and host-header abuse that built on this fix.
  2. Audit APPEND_SLASH and PREPEND_WWW usage. Confirm you understand where these settings are enabled and whether your deployment relies on them. Disabling APPEND_SLASH removes this specific redirect vector entirely, though it may change URL-handling behavior your application depends on, so treat it as a mitigation to evaluate rather than a default recommendation.
  3. Set ALLOWED_HOSTS correctly and restrictively. While ALLOWED_HOSTS doesn't directly govern the CommonMiddleware redirect path in the vulnerable versions, a tightly scoped ALLOWED_HOSTS list is a general defense against Host-header-driven redirect and cache-poisoning issues in Django and reduces the attack surface for related classes of bugs.
  4. Review reverse-proxy and load-balancer forwarded-host handling. Open redirects in framework middleware are often compounded by proxies that forward untrusted Host or X-Forwarded-Host headers unchanged. Confirm your proxy layer normalizes or validates these headers before they reach Django.
  5. Re-scan dependency manifests. Because this vulnerability lives in the framework itself rather than a third-party package, requirements.txt, Pipfile.lock, poetry.lock, and container base images all need to be checked for the affected Django version ranges — not just your direct application dependencies.
  6. Watch for the pattern in custom middleware. Teams that wrote their own slash-normalization, canonical-domain, or "www" redirect middleware modeled on Django's approach should independently verify their code validates the redirect target's host, since the same class of mistake is easy to reintroduce in custom code even after upgrading Django.

How Safeguard Helps

CVE-2018-14574 is a good illustration of why supply chain visibility has to extend past "is this package installed" and into "which version, where, and is it actually reachable in a vulnerable configuration." Safeguard is built to close exactly that gap for cases like this one:

  • Continuous SBOM and dependency inventory across your repositories and running services means Safeguard already knows every place Django is deployed and at which version — so when a framework-level CVE like this surfaces, you get an immediate, accurate list of affected applications instead of having to grep manifests across dozens of repos under time pressure.
  • Version-range-aware vulnerability matching flags components sitting in the affected 1.11.x-before-1.11.15 or 2.0.x-before-2.0.8 windows specifically, rather than generic "Django has a CVE" noise that forces manual triage to figure out if you're actually exposed.
  • Configuration-aware risk context helps prioritize findings like this one: an open redirect gated behind a default setting (APPEND_SLASH) is a different risk profile than one requiring nonstandard configuration, and Safeguard's risk scoring accounts for exploitability factors like required user interaction and default-enabled conditions rather than surfacing every CVE at the same priority.
  • EPSS and KEV enrichment on every finding means your team can see, at a glance, that a given CVE has no known active exploitation and a low predicted exploitation probability — so a Medium-severity open redirect from 2018 doesn't compete for the same urgency budget as an actively exploited remote code execution bug, and your patch cycles reflect real-world risk rather than raw severity labels.
  • Remediation tracking and drift detection confirm the fix actually landed and stays landed — flagging any redeploy, rollback, or new service that reintroduces an unpatched Django version, so a fix that shipped in 2018 doesn't quietly regress in a forgotten staging environment or legacy service years later.

Old CVEs like this one rarely disappear from real environments; they linger in stale container images, vendored dependencies, and services nobody has touched since the original release. Safeguard's job is to make sure "we already knew about this" is always followed by "and we already fixed it everywhere," not just in the repository someone happened to remember to check.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.