[Django] #36795: Always quote user-provided aliases

23 views
Skip to first unread message

Django

unread,
Dec 11, 2025, 3:22:07 PM12/11/25
to django-...@googlegroups.com
#36795: Always quote user-provided aliases
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Type:
| Cleanup/optimization
Status: new | Component: Database
| layer (models, ORM)
Version: dev | Severity: Normal
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
As part of evaluating CVE-2025-13372 (mitigated in
5b90ca1e7591fa36fccf2d6dad67cf1477e6293e), Simon Charette observed that we
could reduce the potential for mistakes in this area by systematically
quoting user-provided aliases, instead of checking aliases against
`FORBIDDEN_ALIAS_PATTERN`:

> ... the root of the problem here is that we don't perform alias quoting
when they are provided by the user as on some backends they are treated
differently mainly regarding case (e.g. "foo" != "FOO" on Postgres). If we
did systematically quote aliases most of the FORBIDDEN_ALIAS_PATTERN logic
would be unnecessary (as long as we escape quotes in aliases) as aliases
would always be quoted.

> There is work to do and deprecation cycles to put in place (mainly
regarding extra(...) usage) if we want to venture that way (particularly
regarding subqueries) but try ... the following patch

{{{#!diff
diff --git a/django/db/models/expressions.py
b/django/db/models/expressions.py
index 0d47366d2c..c6eb9750e2 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -1322,7 +1322,7 @@ def __repr__(self):
def as_sql(self, compiler, connection):
alias, column = self.alias, self.target.column
identifiers = (alias, column) if alias else (column,)
- sql = ".".join(map(compiler.quote_name_unless_alias,
identifiers))
+ sql = ".".join(map(compiler.quote_name, identifiers))
return sql, ()

def relabeled_clone(self, relabels):
diff --git a/django/db/models/sql/datastructures.py
b/django/db/models/sql/datastructures.py
index 5314d37a1a..5ef5efbcfc 100644
--- a/django/db/models/sql/datastructures.py
+++ b/django/db/models/sql/datastructures.py
@@ -125,7 +125,7 @@ def as_sql(self, compiler, connection):
sql = "%s %s%s ON (%s)" % (
self.join_type,
qn(self.table_name),
- alias_str,
+ qn(alias_str),
on_clause_sql,
)
return sql, params
}}}

(Notice that is *not* a complete patch but rather a sketch to send a
contributor down a fruitful path.)

With agreement from the Security Team, Simon's recommendation was that
after the mitigation we should:

> ... investigate how much work it would be to treat user vs system
generated aliases differently in a public ticket.
--
Ticket URL: <https://code.djangoproject.com/ticket/36795>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Dec 11, 2025, 3:25:41 PM12/11/25
to django-...@googlegroups.com
#36795: Always quote user-provided aliases
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Simon
Type: | Charette
Cleanup/optimization | Status: assigned
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Simon Charette):

* owner: (none) => Simon Charette
* status: new => assigned

Comment:

Thanks for creating an actionable item out of this Jacob, I can self
assign and look into it during the holidays.
--
Ticket URL: <https://code.djangoproject.com/ticket/36795#comment:1>

Django

unread,
Dec 11, 2025, 3:25:48 PM12/11/25
to django-...@googlegroups.com
#36795: Always quote user-provided aliases
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Simon
Type: | Charette
Cleanup/optimization | Status: assigned
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Simon Charette):

* stage: Unreviewed => Accepted

--
Ticket URL: <https://code.djangoproject.com/ticket/36795#comment:2>

Django

unread,
Dec 20, 2025, 8:39:23 AM12/20/25
to django-...@googlegroups.com
#36795: Always quote user-provided aliases
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Rudraksha
Type: | Dwivedi
Cleanup/optimization | Status: assigned
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Rudraksha Dwivedi):

* owner: Simon Charette => Rudraksha Dwivedi

--
Ticket URL: <https://code.djangoproject.com/ticket/36795#comment:3>

Django

unread,
Dec 20, 2025, 8:42:32 AM12/20/25
to django-...@googlegroups.com
#36795: Always quote user-provided aliases
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Simon
Type: | Charette
Cleanup/optimization | Status: assigned
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Jacob Walls):

* owner: Rudraksha Dwivedi => Simon Charette

--
Ticket URL: <https://code.djangoproject.com/ticket/36795#comment:4>

Django

unread,
Dec 28, 2025, 6:31:56 AM12/28/25
to django-...@googlegroups.com
#36795: Always quote user-provided aliases
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Simon
Type: | Charette
Cleanup/optimization | Status: assigned
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Rudraksha Dwivedi):

Hey!
sorry about abruption here I genuinely thought I understood the Issue,
clearly i didn't
--
Ticket URL: <https://code.djangoproject.com/ticket/36795#comment:5>

Django

unread,
Mar 15, 2026, 11:50:55 PMMar 15
to django-...@googlegroups.com
#36795: Always quote user-provided aliases
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Simon
Type: | Charette
Cleanup/optimization | Status: assigned
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Simon Charette):

* has_patch: 0 => 1

--
Ticket URL: <https://code.djangoproject.com/ticket/36795#comment:6>

Django

unread,
Mar 18, 2026, 9:49:14 AMMar 18
to django-...@googlegroups.com
#36795: Always quote user-provided aliases
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Simon
Type: | Charette
Cleanup/optimization | Status: assigned
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Jacob Walls):

* stage: Accepted => Ready for checkin

--
Ticket URL: <https://code.djangoproject.com/ticket/36795#comment:7>

Django

unread,
Mar 19, 2026, 12:24:28 PMMar 19
to django-...@googlegroups.com
#36795: Always quote user-provided aliases
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Simon
Type: | Charette
Cleanup/optimization | Status: closed
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution: fixed
Keywords: | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Jacob Walls <jacobtylerwalls@…>):

In [changeset:"5146449a38222dc74f8f1ba88a7a7ef681e93101" 5146449a]:
{{{#!CommitTicketReference repository=""
revision="5146449a38222dc74f8f1ba88a7a7ef681e93101"
Refs #36795 -- Removed unnecessary
prohibits_dollar_signs_in_column_aliases feature flag.

Now that user provided aliases are systematically quoted there is no need
to
disallow the usage of the dollar sign on Postgres.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/36795#comment:9>

Django

unread,
Mar 19, 2026, 12:24:29 PMMar 19
to django-...@googlegroups.com
#36795: Always quote user-provided aliases
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Simon
Type: | Charette
Cleanup/optimization | Status: closed
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution: fixed
Keywords: | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Jacob Walls <jacobtylerwalls@…>):

In [changeset:"1786cd881ff4ad9458d56180ae555d92c14e5af8" 1786cd88]:
{{{#!CommitTicketReference repository=""
revision="1786cd881ff4ad9458d56180ae555d92c14e5af8"
Refs #36795 -- Deprecated SQLCompiler.quote_name_unless_alias().

It has been superseded with .quote_name(), which ensures aliases are
always quoted.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/36795#comment:10>

Django

unread,
Mar 19, 2026, 12:24:30 PMMar 19
to django-...@googlegroups.com
#36795: Always quote user-provided aliases
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Simon
Type: | Charette
Cleanup/optimization | Status: closed
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution: fixed
Keywords: | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Jacob Walls <jacobtylerwalls@…>):

* resolution: => fixed
* status: assigned => closed

Comment:

In [changeset:"f05fac88c4699c6d04a8f1ac3328cf6c7bd39228" f05fac8]:
{{{#!CommitTicketReference repository=""
revision="f05fac88c4699c6d04a8f1ac3328cf6c7bd39228"
Fixed #36795 -- Enforced quoting of all database object names.

This ensures all database identifiers are quoted independently of their
orign
and most importantly that user provided aliases through annotate() and
alias()
which paves the way for dropping the allow list of characters such aliases
can
contain.

This will require adjustments to raw SQL interfaces such as RawSQL that
might
make reference to ORM managed annotations as these will now be quoted.

The `SQLCompiler.quote_name_unless_alias` method is kept for now as an
alias
for the newly introduced `.quote_name` method but will be duly deprecated
in
a follow up commit.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/36795#comment:8>
Reply all
Reply to author
Forward
0 new messages