Potential SQL injection via QuerySet.order_by and FilteredRelation
Description
An issue was discovered in 6.0 before 6.0.2, 5.2 before 5.2.11, and 4.2 before 4.2.28. .QuerySet.order_by() is subject to SQL injection in column aliases containing periods when the same alias is, using a suitably crafted dictionary, with dictionary expansion, used in FilteredRelation. Earlier, unsupported Django series (such as 5.0.x, 4.1.x, and 3.2.x) were not evaluated and may also be affected. Django would like to thank Solomon Kebede for reporting this issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
DjangoPyPI | >= 6.0a1, < 6.0.2 | 6.0.2 |
DjangoPyPI | >= 5.2a1, < 5.2.11 | 5.2.11 |
DjangoPyPI | >= 4.2a1, < 4.2.28 | 4.2.28 |
Affected products
1- Range: 4.2, 4.2.1, 4.2.10, …
Patches
2005d60d97c4dRefs CVE-2026-1312 -- Raised ValueError when FilteredRelation aliases contain periods.
3 files changed · +26 −3
django/db/models/sql/query.py+5 −0 modified@@ -1720,6 +1720,11 @@ def _add_q( return target_clause, needed_inner def add_filtered_relation(self, filtered_relation, alias): + if "." in alias: + raise ValueError( + "FilteredRelation doesn't support aliases with periods " + "(got %r)." % alias + ) self.check_alias(alias) filtered_relation.alias = alias relation_lookup_parts, relation_field_parts, _ = self.solve_lookup_type(
tests/filtered_relation/tests.py+13 −0 modified@@ -216,6 +216,19 @@ def test_internal_queryset_alias_mapping(self): str(queryset.query), ) + def test_period_forbidden(self): + msg = ( + "FilteredRelation doesn't support aliases with periods (got 'book.alice')." + ) + with self.assertRaisesMessage(ValueError, msg): + Author.objects.annotate( + **{ + "book.alice": FilteredRelation( + "book", condition=Q(book__title__iexact="poem by alice") + ) + } + ) + def test_multiple(self): qs = ( Author.objects.annotate(
tests/ordering/tests.py+8 −3 modified@@ -18,7 +18,6 @@ When, ) from django.db.models.functions import Length, Upper -from django.db.utils import DatabaseError from django.test import TestCase from .models import ( @@ -411,13 +410,19 @@ def test_alias_with_period_shadows_table_name(self): self.assertNotEqual(qs[0].headline, "Backdated") relation = FilteredRelation("author") - qs2 = Article.objects.annotate(**{crafted: relation}).order_by(crafted) - with self.assertRaises(DatabaseError): + msg = ( + "FilteredRelation doesn't support aliases with periods " + "(got 'ordering_article.pub_date')." + ) + with self.assertRaisesMessage(ValueError, msg): + qs2 = Article.objects.annotate(**{crafted: relation}).order_by(crafted) # Before, unlike F(), which causes ordering expressions to be # replaced by ordinals like n in ORDER BY n, these were ordered by # pub_date instead of author. # The Article model orders by -pk, so sorting on author will place # first any article by author2 instead of the backdated one. + # This assertion is reachable if FilteredRelation.__init__() starts + # supporting periods in aliases in the future. self.assertNotEqual(qs2[0].headline, "Backdated") def test_order_by_pk(self):
69065ca869b0Fixed CVE-2026-1312 -- Protected order_by() from SQL injection via aliases with periods.
5 files changed · +56 −1
django/db/models/sql/compiler.py+1 −1 modified@@ -433,7 +433,7 @@ def _order_by_pairs(self): yield OrderBy(expr, descending=descending), False continue - if "." in field: + if "." in field and field in self.query.extra_order_by: # This came in through an extra(order_by=...) addition. Pass it # on verbatim. table, col = col.split(".", 1)
docs/releases/4.2.28.txt+10 −0 modified@@ -66,3 +66,13 @@ expansion, as the ``**kwargs`` passed to :meth:`.QuerySet.annotate`, This issue has severity "high" according to the :ref:`Django security policy <security-disclosure>`. + +CVE-2026-1312: Potential SQL injection via ``QuerySet.order_by`` and ``FilteredRelation`` +========================================================================================= + +:meth:`.QuerySet.order_by` was subject to SQL injection in column aliases +containing periods when the same alias was, using a suitably crafted +dictionary, with dictionary expansion, used in :class:`.FilteredRelation`. + +This issue has severity "high" according to the :ref:`Django security policy +<security-disclosure>`.
docs/releases/5.2.11.txt+10 −0 modified@@ -66,3 +66,13 @@ expansion, as the ``**kwargs`` passed to :meth:`.QuerySet.annotate`, This issue has severity "high" according to the :ref:`Django security policy <security-disclosure>`. + +CVE-2026-1312: Potential SQL injection via ``QuerySet.order_by`` and ``FilteredRelation`` +========================================================================================= + +:meth:`.QuerySet.order_by` was subject to SQL injection in column aliases +containing periods when the same alias was, using a suitably crafted +dictionary, with dictionary expansion, used in :class:`.FilteredRelation`. + +This issue has severity "high" according to the :ref:`Django security policy +<security-disclosure>`.
docs/releases/6.0.2.txt+10 −0 modified@@ -67,6 +67,16 @@ expansion, as the ``**kwargs`` passed to :meth:`.QuerySet.annotate`, This issue has severity "high" according to the :ref:`Django security policy <security-disclosure>`. +CVE-2026-1312: Potential SQL injection via ``QuerySet.order_by`` and ``FilteredRelation`` +========================================================================================= + +:meth:`.QuerySet.order_by` was subject to SQL injection in column aliases +containing periods when the same alias was, using a suitably crafted +dictionary, with dictionary expansion, used in :class:`.FilteredRelation`. + +This issue has severity "high" according to the :ref:`Django security policy +<security-disclosure>`. + Bugfixes ========
tests/ordering/tests.py+25 −0 modified@@ -8,6 +8,7 @@ Count, DateTimeField, F, + FilteredRelation, IntegerField, Max, OrderBy, @@ -17,6 +18,7 @@ When, ) from django.db.models.functions import Length, Upper +from django.db.utils import DatabaseError from django.test import TestCase from .models import ( @@ -395,6 +397,29 @@ def test_extra_ordering_with_table_name(self): attrgetter("headline"), ) + def test_alias_with_period_shadows_table_name(self): + """ + Aliases with periods are not confused for table names from extra(). + """ + Article.objects.update(author=self.author_2) + Article.objects.create( + headline="Backdated", pub_date=datetime(1900, 1, 1), author=self.author_1 + ) + crafted = "ordering_article.pub_date" + + qs = Article.objects.annotate(**{crafted: F("author")}).order_by("-" + crafted) + self.assertNotEqual(qs[0].headline, "Backdated") + + relation = FilteredRelation("author") + qs2 = Article.objects.annotate(**{crafted: relation}).order_by(crafted) + with self.assertRaises(DatabaseError): + # Before, unlike F(), which causes ordering expressions to be + # replaced by ordinals like n in ORDER BY n, these were ordered by + # pub_date instead of author. + # The Article model orders by -pk, so sorting on author will place + # first any article by author2 instead of the backdated one. + self.assertNotEqual(qs2[0].headline, "Backdated") + def test_order_by_pk(self): """ 'pk' works as an ordering option in Meta.
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- docs.djangoproject.com/en/dev/releases/security/mitrevendor-advisory
- github.com/advisories/GHSA-6426-9fv3-65x8ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-1312ghsaADVISORY
- www.djangoproject.com/weblog/2026/feb/03/security-releases/mitrevendor-advisory
- docs.djangoproject.com/en/dev/releases/securityghsaWEB
- github.com/django/django/commit/005d60d97c4dfb117503bdb6f2facfcaf9315d84ghsaWEB
- github.com/django/django/commit/69065ca869b0970dff8fdd8fafb390bf8b3bf222ghsaWEB
- groups.google.com/g/django-announceghsamailing-listWEB
- www.djangoproject.com/weblog/2026/feb/03/security-releasesghsaWEB
News mentions
1- How AI Assistants are Moving the Security GoalpostsKrebs on Security · Mar 8, 2026