티스토리 수익 글 보기

티스토리 수익 글 보기

[4.0.x] Fixed CVE-2022-34265 — Protected Trunc(kind)/Extract(lookup_… · django/django@0dc9c01 · GitHub
Skip to content
/ django Public

Commit 0dc9c01

Browse files
committed
[4.0.x] Fixed CVE-2022-34265 — Protected Trunc(kind)/Extract(lookup_name) against SQL injection.
Thanks Takuto Yoshikai (Aeye Security Lab) for the report.
1 parent a2b88d7 commit 0dc9c01

File tree

5 files changed

+61
3
lines changed

5 files changed

+61
3
lines changed

django/db/backends/base/operations.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from django.db.backends import utils
1010
from django.utils import timezone
1111
from django.utils.encoding import force_str
12+
from django.utils.regex_helper import _lazy_re_compile
1213

1314

1415
class BaseDatabaseOperations:
@@ -54,6 +55,8 @@ class BaseDatabaseOperations:
5455
# Prefix for EXPLAIN queries, or None EXPLAIN isn't supported.
5556
explain_prefix = None
5657

58+
extract_trunc_lookup_pattern = _lazy_re_compile(r"[\w\-_()]+")
59+
5760
def __init__(self, connection):
5861
self.connection = connection
5962
self._cache = None

django/db/models/functions/datetime.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ def __init__(self, expression, lookup_name=None, tzinfo=None, **extra):
5151
super().__init__(expression, **extra)
5252

5353
def as_sql(self, compiler, connection):
54+
if not connection.ops.extract_trunc_lookup_pattern.fullmatch(self.lookup_name):
55+
raise ValueError("Invalid lookup_name: %s" % self.lookup_name)
5456
sql, params = compiler.compile(self.lhs)
5557
lhs_output_field = self.lhs.output_field
5658
if isinstance(lhs_output_field, DateTimeField):
@@ -235,6 +237,8 @@ def __init__(
235237
super().__init__(expression, output_field=output_field, **extra)
236238

237239
def as_sql(self, compiler, connection):
240+
if not connection.ops.extract_trunc_lookup_pattern.fullmatch(self.kind):
241+
raise ValueError("Invalid kind: %s" % self.kind)
238242
inner_sql, inner_params = compiler.compile(self.lhs)
239243
tzname = None
240244
if isinstance(self.lhs.output_field, DateTimeField):

docs/releases/3.2.14.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,14 @@ Django 3.2.14 release notes
55
*July 4, 2022*
66

77
Django 3.2.14 fixes a security issue with severity "high" in 3.2.13.
8+
9+
CVE-2022-34265: Potential SQL injection via ``Trunc(kind)`` and ``Extract(lookup_name)`` arguments
10+
==================================================================================================
11+
12+
:class:`Trunc() <django.db.models.functions.Trunc>` and
13+
:class:`Extract() <django.db.models.functions.Extract>` database functions were
14+
subject to SQL injection if untrusted data was used as a
15+
``kind``/``lookup_name`` value.
16+
17+
Applications that constrain the lookup name and kind choice to a known safe
18+
list are unaffected.

docs/releases/4.0.6.txt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ Django 4.0.6 release notes
66

77
Django 4.0.6 fixes a security issue with severity "high" in 4.0.5.
88

9-
Bugfixes
10-
========
9+
CVE-2022-34265: Potential SQL injection via ``Trunc(kind)`` and ``Extract(lookup_name)`` arguments
10+
==================================================================================================
1111

12-
* ...
12+
:class:`Trunc() <django.db.models.functions.Trunc>` and
13+
:class:`Extract() <django.db.models.functions.Extract>` database functions were
14+
subject to SQL injection if untrusted data was used as a
15+
``kind``/``lookup_name`` value.
16+
17+
Applications that constrain the lookup name and kind choice to a known safe
18+
list are unaffected.

tests/db_functions/datetime/test_extract_trunc.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,23 @@ def test_extract_year_lessthan_lookup(self):
235235
self.assertEqual(qs.count(), 1)
236236
self.assertGreaterEqual(str(qs.query).lower().count("extract"), 2)
237237

238+
def test_extract_lookup_name_sql_injection(self):
239+
start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321)
240+
end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123)
241+
if settings.USE_TZ:
242+
start_datetime = timezone.make_aware(start_datetime)
243+
end_datetime = timezone.make_aware(end_datetime)
244+
self.create_model(start_datetime, end_datetime)
245+
self.create_model(end_datetime, start_datetime)
246+
247+
msg = "Invalid lookup_name: "
248+
with self.assertRaisesMessage(ValueError, msg):
249+
DTModel.objects.filter(
250+
start_datetime__year=Extract(
251+
"start_datetime", "day' FROM start_datetime)) OR 1=1;--"
252+
)
253+
).exists()
254+
238255
def test_extract_func(self):
239256
start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321)
240257
end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123)
@@ -899,6 +916,23 @@ def test_extract_second_func(self):
899916
2,
900917
)
901918

919+
def test_trunc_lookup_name_sql_injection(self):
920+
start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321)
921+
end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123)
922+
if settings.USE_TZ:
923+
start_datetime = timezone.make_aware(start_datetime)
924+
end_datetime = timezone.make_aware(end_datetime)
925+
self.create_model(start_datetime, end_datetime)
926+
self.create_model(end_datetime, start_datetime)
927+
msg = "Invalid kind: "
928+
with self.assertRaisesMessage(ValueError, msg):
929+
DTModel.objects.filter(
930+
start_datetime__date=Trunc(
931+
"start_datetime",
932+
"year', start_datetime)) OR 1=1;--",
933+
)
934+
).exists()
935+
902936
def test_trunc_func(self):
903937
start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321)
904938
end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123)

0 commit comments

Comments
 (0)