Commit 985434fb authored by Erik Romijn's avatar Erik Romijn Committed by Tim Graham
Browse files

[1.5.x] Fixed queries that may return unexpected results on MySQL due to typecasting.

This is a security fix. Disclosure will follow shortly.

Backport of 75c0d4ea from master
parent 6872f427
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -934,6 +934,12 @@ class FilePathField(Field):
        kwargs['max_length'] = kwargs.get('max_length', 100)
        Field.__init__(self, verbose_name, name, **kwargs)

    def get_prep_value(self, value):
        value = super(FilePathField, self).get_prep_value(value)
        if value is None:
            return None
        return six.text_type(value)

    def formfield(self, **kwargs):
        defaults = {
            'path': self.path,
@@ -1035,6 +1041,12 @@ class IPAddressField(Field):
        kwargs['max_length'] = 15
        Field.__init__(self, *args, **kwargs)

    def get_prep_value(self, value):
        value = super(IPAddressField, self).get_prep_value(value)
        if value is None:
            return None
        return six.text_type(value)

    def get_internal_type(self):
        return "IPAddressField"

@@ -1072,12 +1084,14 @@ class GenericIPAddressField(Field):
        return value or None

    def get_prep_value(self, value):
        if value is None:
            return value
        if value and ':' in value:
            try:
                return clean_ipv6_address(value, self.unpack_ipv4)
            except exceptions.ValidationError:
                pass
        return value
        return six.text_type(value)

    def formfield(self, **kwargs):
        defaults = {'form_class': forms.GenericIPAddressField}
+10 −0
Original line number Diff line number Diff line
@@ -501,6 +501,16 @@ For example::
            return ''.join([''.join(l) for l in (value.north,
                    value.east, value.south, value.west)])

.. warning::

    If your custom field uses the ``CHAR``, ``VARCHAR`` or ``TEXT``
    types for MySQL, you must make sure that :meth:`.get_prep_value`
    always returns a string type. MySQL performs flexible and unexpected
    matching when a query is performed on these types and the provided
    value is an integer, which can cause queries to include unexpected
    objects in their results. This problem cannot occur if you always
    return a string type from :meth:`.get_prep_value`.

Converting query values to database values
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+16 −0
Original line number Diff line number Diff line
@@ -429,6 +429,22 @@ MySQL does not support the ``NOWAIT`` option to the ``SELECT ... FOR UPDATE``
statement. If ``select_for_update()`` is used with ``nowait=True`` then a
``DatabaseError`` will be raised.

Automatic typecasting can cause unexpected results
--------------------------------------------------

When performing a query on a string type, but with an integer value, MySQL will
coerce the types of all values in the table to an integer before performing the
comparison. If your table contains the values ``'abc'``, ``'def'`` and you
query for ``WHERE mycolumn=0``, both rows will match. Similarly, ``WHERE mycolumn=1``
will match the value ``'abc1'``. Therefore, string type fields included in Django
will always cast the value to a string before using it in a query.

If you implement custom model fields that inherit from :class:`~django.db.models.Field`
directly, are overriding :meth:`~django.db.models.Field.get_prep_value`, or use
:meth:`extra() <django.db.models.query.QuerySet.extra>` or
:meth:`raw() <django.db.models.Manager.raw>`, you should ensure that you
perform the appropriate typecasting.

.. _sqlite-notes:

SQLite notes
+10 −0
Original line number Diff line number Diff line
@@ -1068,6 +1068,16 @@ of the arguments is required, but you should use at least one of them.

      Entry.objects.extra(where=['headline=%s'], params=['Lennon'])

.. warning::

    If you are performing queries on MySQL, note that MySQL's silent type coercion
    may cause unexpected results when mixing types. If you query on a string
    type column, but with an integer value, MySQL will coerce the types of all values
    in the table to an integer before performing the comparison. For example, if your
    table contains the values ``'abc'``, ``'def'`` and you query for ``WHERE mycolumn=0``,
    both rows will match. To prevent this, perform the correct typecasting
    before using the value in a query.

defer
~~~~~

+10 −0
Original line number Diff line number Diff line
@@ -66,6 +66,16 @@ options that make it very powerful.
    database, but does nothing to enforce that. If the query does not
    return rows, a (possibly cryptic) error will result.

.. warning::

    If you are performing queries on MySQL, note that MySQL's silent type coercion
    may cause unexpected results when mixing types. If you query on a string
    type column, but with an integer value, MySQL will coerce the types of all values
    in the table to an integer before performing the comparison. For example, if your
    table contains the values ``'abc'``, ``'def'`` and you query for ``WHERE mycolumn=0``,
    both rows will match. To prevent this, perform the correct typecasting
    before using the value in a query.

Mapping query fields to model fields
------------------------------------

Loading