Skip to content
Permalink
Browse files

[1.11.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.
  • Loading branch information...
felixxm authored and carltongibson committed Jul 22, 2019
1 parent 52479ac commit ed682a24fca774818542757651bfba576c3fc3ef
@@ -86,7 +86,7 @@ def __init__(self, key_name, *args, **kwargs):

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


class KeyTransformFactory(object):
@@ -104,12 +104,10 @@ def as_sql(self, compiler, connection):
if len(key_transforms) > 1:
return "(%s %s %%s)" % (lhs, self.nested_operator), [key_transforms] + params
try:
int(self.key_name)
lookup = int(self.key_name)
except ValueError:
lookup = "'%s'" % self.key_name
else:
lookup = "%s" % self.key_name
return "(%s %s %s)" % (lhs, self.operator, lookup), params
lookup = self.key_name
return '(%s %s %%s)' % (lhs, self.operator), [lookup] + params


class KeyTextTransform(KeyTransform):
@@ -36,3 +36,12 @@ Remember that absolutely NO guarantee is provided about the results of
``strip_tags()`` being HTML safe. So NEVER mark safe the result of a
``strip_tags()`` call without escaping it first, for example with
:func:`django.utils.html.escape`.

CVE-2019-14234: SQL injection possibility in key and index lookups for ``JSONField``/``HStoreField``
====================================================================================================

:lookup:`Key and index lookups <jsonfield.key>` for
:class:`~django.contrib.postgres.fields.JSONField` and :lookup:`key lookups
<hstorefield.key>` for :class:`~django.contrib.postgres.fields.HStoreField`
were subject to SQL injection, using a suitably crafted dictionary, with
dictionary expansion, as the ``**kwargs`` passed to ``QuerySet.filter()``.
@@ -4,8 +4,9 @@
import json

from django.core import exceptions, serializers
from django.db import connection
from django.forms import Form
from django.test.utils import modify_settings
from django.test.utils import CaptureQueriesContext, modify_settings

from . import PostgreSQLTestCase
from .models import HStoreModel
@@ -167,6 +168,18 @@ def test_usage_in_subquery(self):
self.objs[:2]
)

def test_key_sql_injection(self):
with CaptureQueriesContext(connection) as queries:
self.assertFalse(
HStoreModel.objects.filter(**{
"field__test' = 'a') OR 1 = 1 OR ('d": 'x',
}).exists()
)
self.assertIn(
"""."field" -> 'test'' = ''a'') OR 1 = 1 OR (''d') = 'x' """,
queries[0]['sql'],
)


class TestSerialization(HStoreTestCase):
test_data = ('[{"fields": {"field": "{\\"a\\": \\"b\\"}"}, '
@@ -6,8 +6,10 @@

from django.core import exceptions, serializers
from django.core.serializers.json import DjangoJSONEncoder
from django.db import connection
from django.forms import CharField, Form, widgets
from django.test import skipUnlessDBFeature
from django.test.utils import CaptureQueriesContext
from django.utils.html import escape

from . import PostgreSQLTestCase
@@ -263,6 +265,18 @@ def test_regex(self):
def test_iregex(self):
self.assertTrue(JSONModel.objects.filter(field__foo__iregex=r'^bAr$').exists())

def test_key_sql_injection(self):
with CaptureQueriesContext(connection) as queries:
self.assertFalse(
JSONModel.objects.filter(**{
"""field__test' = '"a"') OR 1 = 1 OR ('d""": 'x',
}).exists()
)
self.assertIn(
"""."field" -> 'test'' = ''"a"'') OR 1 = 1 OR (''d') = '"x"' """,
queries[0]['sql'],
)


@skipUnlessDBFeature('has_jsonb_datatype')
class TestSerialization(PostgreSQLTestCase):

0 comments on commit ed682a2

Please sign in to comment.
You can’t perform that action at this time.