Opened 17 minutes ago
Last modified 13 minutes ago
#36795 assigned Cleanup/optimization
Always quote user-provided aliases
| Reported by: | Jacob Walls | Owned by: | Simon Charette |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | dev |
| Severity: | Normal | Keywords: | |
| Cc: | Simon Charette | Triage Stage: | Accepted |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
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
-
django/db/models/expressions.py
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index 0d47366d2c..c6eb9750e2 100644
a b def __repr__(self): 1322 1322 def as_sql(self, compiler, connection): 1323 1323 alias, column = self.alias, self.target.column 1324 1324 identifiers = (alias, column) if alias else (column,) 1325 sql = ".".join(map(compiler.quote_name _unless_alias, identifiers))1325 sql = ".".join(map(compiler.quote_name, identifiers)) 1326 1326 return sql, () 1327 1327 1328 1328 def relabeled_clone(self, relabels): -
django/db/models/sql/datastructures.py
diff --git a/django/db/models/sql/datastructures.py b/django/db/models/sql/datastructures.py index 5314d37a1a..5ef5efbcfc 100644
a b def as_sql(self, compiler, connection): 125 125 sql = "%s %s%s ON (%s)" % ( 126 126 self.join_type, 127 127 qn(self.table_name), 128 alias_str,128 qn(alias_str), 129 129 on_clause_sql, 130 130 ) 131 131 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.
Change History (2)
comment:1 by , 13 minutes ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
comment:2 by , 13 minutes ago
| Triage Stage: | Unreviewed → Accepted |
|---|
Thanks for creating an actionable item out of this Jacob, I can self assign and look into it during the holidays.