티스토리 수익 글 보기

티스토리 수익 글 보기

[2.2.x] Fixed CVE-2019-14234 — Protected JSONField/HStoreField key a… · django/django@4f5b58f · GitHub
Skip to content

Commit 4f5b58f

Browse files
felixxmcarltongibson
authored andcommitted
[2.2.x] Fixed CVE-2019-14234 — Protected JSONField/HStoreField key and index lookups against SQL injection.
Thanks to Sage M. Abdullah for the report and initial patch. Thanks Florian Apolloner for reviews.
1 parent e34f3c0 commit 4f5b58f

File tree

7 files changed

+59
8
lines changed

7 files changed

+59
8
lines changed

django/contrib/postgres/fields/hstore.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def __init__(self, key_name, *args, **kwargs):
8686

8787
def as_sql(self, compiler, connection):
8888
lhs, params = compiler.compile(self.lhs)
89-
return "(%s -> '%s')" % (lhs, self.key_name), params
89+
return '(%s -> %%s)' % lhs, [self.key_name] + params
9090

9191

9292
class KeyTransformFactory:

django/contrib/postgres/fields/jsonb.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,10 @@ def as_sql(self, compiler, connection):
109109
if len(key_transforms) > 1:
110110
return "(%s %s %%s)" % (lhs, self.nested_operator), [key_transforms] + params
111111
try:
112-
int(self.key_name)
112+
lookup = int(self.key_name)
113113
except ValueError:
114-
lookup = "'%s'" % self.key_name
115-
else:
116-
lookup = "%s" % self.key_name
117-
return "(%s %s %s)" % (lhs, self.operator, lookup), params
114+
lookup = self.key_name
115+
return '(%s %s %%s)' % (lhs, self.operator), [lookup] + params
118116

119117

120118
class KeyTextTransform(KeyTransform):

docs/releases/1.11.23.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,12 @@ Remember that absolutely NO guarantee is provided about the results of
3636
``strip_tags()`` being HTML safe. So NEVER mark safe the result of a
3737
``strip_tags()`` call without escaping it first, for example with
3838
:func:`django.utils.html.escape`.
39+
40+
CVE-2019-14234: SQL injection possibility in key and index lookups for ``JSONField``/``HStoreField``
41+
====================================================================================================
42+
43+
:lookup:`Key and index lookups <jsonfield.key>` for
44+
:class:`~django.contrib.postgres.fields.JSONField` and :lookup:`key lookups
45+
<hstorefield.key>` for :class:`~django.contrib.postgres.fields.HStoreField`
46+
were subject to SQL injection, using a suitably crafted dictionary, with
47+
dictionary expansion, as the ``**kwargs`` passed to ``QuerySet.filter()``.

docs/releases/2.1.11.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,12 @@ Remember that absolutely NO guarantee is provided about the results of
3636
``strip_tags()`` being HTML safe. So NEVER mark safe the result of a
3737
``strip_tags()`` call without escaping it first, for example with
3838
:func:`django.utils.html.escape`.
39+
40+
CVE-2019-14234: SQL injection possibility in key and index lookups for ``JSONField``/``HStoreField``
41+
====================================================================================================
42+
43+
:lookup:`Key and index lookups <jsonfield.key>` for
44+
:class:`~django.contrib.postgres.fields.JSONField` and :lookup:`key lookups
45+
<hstorefield.key>` for :class:`~django.contrib.postgres.fields.HStoreField`
46+
were subject to SQL injection, using a suitably crafted dictionary, with
47+
dictionary expansion, as the ``**kwargs`` passed to ``QuerySet.filter()``.

docs/releases/2.2.4.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ Remember that absolutely NO guarantee is provided about the results of
3737
``strip_tags()`` call without escaping it first, for example with
3838
:func:`django.utils.html.escape`.
3939

40+
CVE-2019-14234: SQL injection possibility in key and index lookups for ``JSONField``/``HStoreField``
41+
====================================================================================================
42+
43+
:lookup:`Key and index lookups <jsonfield.key>` for
44+
:class:`~django.contrib.postgres.fields.JSONField` and :lookup:`key lookups
45+
<hstorefield.key>` for :class:`~django.contrib.postgres.fields.HStoreField`
46+
were subject to SQL injection, using a suitably crafted dictionary, with
47+
dictionary expansion, as the ``**kwargs`` passed to ``QuerySet.filter()``.
48+
4049
Bugfixes
4150
========
4251

tests/postgres_tests/test_hstore.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import json
22

33
from django.core import checks, exceptions, serializers
4+
from django.db import connection
45
from django.forms import Form
5-
from django.test.utils import isolate_apps
6+
from django.test.utils import CaptureQueriesContext, isolate_apps
67

78
from . import PostgreSQLSimpleTestCase, PostgreSQLTestCase
89
from .models import HStoreModel, PostgreSQLModel
@@ -185,6 +186,18 @@ def test_usage_in_subquery(self):
185186
self.objs[:2]
186187
)
187188

189+
def test_key_sql_injection(self):
190+
with CaptureQueriesContext(connection) as queries:
191+
self.assertFalse(
192+
HStoreModel.objects.filter(**{
193+
"field__test' = 'a') OR 1 = 1 OR ('d": 'x',
194+
}).exists()
195+
)
196+
self.assertIn(
197+
"""."field" -> 'test'' = ''a'') OR 1 = 1 OR (''d') = 'x' """,
198+
queries[0]['sql'],
199+
)
200+
188201

189202
@isolate_apps('postgres_tests')
190203
class TestChecks(PostgreSQLSimpleTestCase):

tests/postgres_tests/test_json.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55

66
from django.core import checks, exceptions, serializers
77
from django.core.serializers.json import DjangoJSONEncoder
8+
from django.db import connection
89
from django.db.models import Count, Q
910
from django.forms import CharField, Form, widgets
10-
from django.test.utils import isolate_apps
11+
from django.test.utils import CaptureQueriesContext, isolate_apps
1112
from django.utils.html import escape
1213

1314
from . import PostgreSQLSimpleTestCase, PostgreSQLTestCase
@@ -322,6 +323,18 @@ def test_regex(self):
322323
def test_iregex(self):
323324
self.assertTrue(JSONModel.objects.filter(field__foo__iregex=r'^bAr$').exists())
324325

326+
def test_key_sql_injection(self):
327+
with CaptureQueriesContext(connection) as queries:
328+
self.assertFalse(
329+
JSONModel.objects.filter(**{
330+
"""field__test' = '"a"') OR 1 = 1 OR ('d""": 'x',
331+
}).exists()
332+
)
333+
self.assertIn(
334+
"""."field" -> 'test'' = ''"a"'') OR 1 = 1 OR (''d') = '"x"' """,
335+
queries[0]['sql'],
336+
)
337+
325338

326339
@isolate_apps('postgres_tests')
327340
class TestChecks(PostgreSQLSimpleTestCase):

0 commit comments

Comments
 (0)