티스토리 수익 글 보기

티스토리 수익 글 보기

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

Commit a9010fe

Browse files
committed
[3.2.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 3acf156 commit a9010fe

File tree

4 files changed

+52
0
lines changed

4 files changed

+52
0
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:
@@ -53,6 +54,8 @@ class BaseDatabaseOperations:
5354
# Prefix for EXPLAIN queries, or None EXPLAIN isn't supported.
5455
explain_prefix = None
5556

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

django/db/models/functions/datetime.py

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

4343
def as_sql(self, compiler, connection):
44+
if not connection.ops.extract_trunc_lookup_pattern.fullmatch(self.lookup_name):
45+
raise ValueError("Invalid lookup_name: %s" % self.lookup_name)
4446
sql, params = compiler.compile(self.lhs)
4547
lhs_output_field = self.lhs.output_field
4648
if isinstance(lhs_output_field, DateTimeField):
@@ -192,6 +194,8 @@ def __init__(self, expression, output_field=None, tzinfo=None, is_dst=None, **ex
192194
super().__init__(expression, output_field=output_field, **extra)
193195

194196
def as_sql(self, compiler, connection):
197+
if not connection.ops.extract_trunc_lookup_pattern.fullmatch(self.kind):
198+
raise ValueError("Invalid kind: %s" % self.kind)
195199
inner_sql, inner_params = compiler.compile(self.lhs)
196200
tzname = None
197201
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.

tests/db_functions/datetime/test_extract_trunc.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,23 @@ def test_extract_year_lessthan_lookup(self):
177177
self.assertEqual(qs.count(), 1)
178178
self.assertGreaterEqual(str(qs.query).lower().count('extract'), 2)
179179

180+
def test_extract_lookup_name_sql_injection(self):
181+
start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321)
182+
end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123)
183+
if settings.USE_TZ:
184+
start_datetime = timezone.make_aware(start_datetime)
185+
end_datetime = timezone.make_aware(end_datetime)
186+
self.create_model(start_datetime, end_datetime)
187+
self.create_model(end_datetime, start_datetime)
188+
189+
msg = "Invalid lookup_name: "
190+
with self.assertRaisesMessage(ValueError, msg):
191+
DTModel.objects.filter(
192+
start_datetime__year=Extract(
193+
"start_datetime", "day' FROM start_datetime)) OR 1=1;--"
194+
)
195+
).exists()
196+
180197
def test_extract_func(self):
181198
start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321)
182199
end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123)
@@ -620,6 +637,23 @@ def test_extract_second_func(self):
620637
)
621638
self.assertEqual(DTModel.objects.filter(start_datetime__second=ExtractSecond('start_datetime')).count(), 2)
622639

640+
def test_trunc_lookup_name_sql_injection(self):
641+
start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321)
642+
end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123)
643+
if settings.USE_TZ:
644+
start_datetime = timezone.make_aware(start_datetime)
645+
end_datetime = timezone.make_aware(end_datetime)
646+
self.create_model(start_datetime, end_datetime)
647+
self.create_model(end_datetime, start_datetime)
648+
msg = "Invalid kind: "
649+
with self.assertRaisesMessage(ValueError, msg):
650+
DTModel.objects.filter(
651+
start_datetime__date=Trunc(
652+
"start_datetime",
653+
"year', start_datetime)) OR 1=1;--",
654+
)
655+
).exists()
656+
623657
def test_trunc_func(self):
624658
start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321)
625659
end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123)

0 commit comments

Comments
 (0)