Recent trends in the work of the Django Security Team
Yesterday, Django issued security releases mitigating six vulnerabilities of varying severity. Django is a secure web framework, and that hasn’t changed. What feels new is the remarkable consistency across the reports we receive now.
Almost every report now is a variation on a prior vulnerability. Instead of uncovering new classes of issues, these reports explore how an underlying pattern from a recent advisory might surface in a similar code path or under a slightly different configuration. These reports are often technically plausible but only sometimes worth fixing. Over time, this has shifted the Security Team’s work away from discovery towards deciding how far a given precedent should extend and whether the impact of the marginal variation rises to the level of a vulnerability.
Take yesterday’s releases:
We patched a “low” severity user enumeration vulnerability in the mod_wsgi authentication handler (CVE 2025-13473). It’s a straightforward variation on CVE 2024-39329, which affected authentication more generally.
We also patched two potential denial-of-service vulnerabilities when handling large, malformed inputs. One exploits inefficient string concatenation in header parsing under ASGI (CVE 2025-14550). Concatenating strings in a loop is known to be slow, and we’ve done fixes in public where the impact is low. The other one (CVE 2026-1285) exploits deeply nested entities. December’s vulnerability in the XML serializer (CVE 2025-64460) was about those very two themes.
Finally, we also patched three potential SQL injection vulnerabilities. One envisioned a developer passing unsanitized user input to a niche feature of the PostGIS backend (CVE 2026-1207), much like CVE 2020-9402. Our security reporting policy assumes that developers are aware of the risks when passing unsanitized user input directly to the ORM. But the division between SQL statements and parameters is well ingrained, and the expectation is that Django will not fail to escape parameters. The last two vulnerabilities (CVE 2026-1287 and CVE 2026-1312) targeted user-controlled column aliases, the latest in a stream of reports stemming from CVE 2022-28346, involving unpacking **kwargs into .filter() and friends, including four security releases in a row in late 2025. You might ask, “who would unpack **kwargs into the ORM?!” But imagine letting users name aggregations in configurable reports. You would have something more like a parameter, and so you would appreciate some protection against crafted inputs.
On top of all that, on a nearly daily basis we get reports duplicating other pending reports, or even reports about vulnerabilities that have already been fixed and publicized. Clearly, reporters are using LLMs to generate (initially) plausible variations.
Security releases come with costs to the community. They interrupt our users’ development workflows, and they also severely interrupt ours.
There are alternatives. The long tail of reports about user-controlled aliases presents an obvious one: we can just re-architect that area. (Thanks to Simon Charette for a pull request doing just that!) Beyond that, there are more drastic alternatives. We can confirm fewer vulnerabilities by placing a higher value on a user's duty to validate inputs, placing a lower value on our prior precedents, or fixing lower severity issues publicly. The risk there is underreacting, or seeing our development workflow disrupted anyway when a decision not to confirm a vulnerability is challenged.
Reporters are clearly benefiting from our commitment to being consistent. For the moment, the Security Team hopes that reacting in a consistent way—even if it means sometimes issuing six patches—outweighs the cost of the security process. It’s something we’re weighing.
As always, keep the responsibly vetted reports coming to security@djangoproject.com.