Fixed #28680 -- Doc'd Func.__init__()'s **extra and as_sql()'s **extra_context aren't escaped.

Thanks Hynek Cernoch for the report and review.
This commit is contained in:
Tim Graham 2017-10-04 11:10:38 -04:00
parent 629dde8db1
commit 1e7dbbdec5
1 changed files with 53 additions and 4 deletions

View File

@ -306,6 +306,11 @@ The ``Func`` API is as follows:
template="%(function)s('', %(expressions)s)",
)
To avoid a SQL injection vulnerability, ``extra_context`` :ref:`must
not contain untrusted user input <avoiding-sql-injection-in-query-expressions>`
as these values are interpolated into the SQL string rather than passed
as query parameters, where the database driver would escape them.
The ``*expressions`` argument is a list of positional expressions that the
function will be applied to. The expressions will be converted to strings,
joined together with ``arg_joiner``, and then interpolated into the ``template``
@ -316,10 +321,15 @@ assumed to be column references and will be wrapped in ``F()`` expressions
while other values will be wrapped in ``Value()`` expressions.
The ``**extra`` kwargs are ``key=value`` pairs that can be interpolated
into the ``template`` attribute. The ``function``, ``template``, and
``arg_joiner`` keywords can be used to replace the attributes of the same name
without having to define your own class. ``output_field`` can be used to define
the expected return type.
into the ``template`` attribute. To avoid a SQL injection vulnerability,
``extra`` :ref:`must not contain untrusted user input
<avoiding-sql-injection-in-query-expressions>` as these values are interpolated
into the SQL string rather than passed as query parameters, where the database
driver would escape them.
The ``function``, ``template``, and ``arg_joiner`` keywords can be used to
replace the attributes of the same name without having to define your own
class. ``output_field`` can be used to define the expected return type.
``Aggregate()`` expressions
---------------------------
@ -1067,6 +1077,45 @@ Let's see how it works::
Yahoo: Internet Company
Django Software Foundation: No Tagline
.. _avoiding-sql-injection-in-query-expressions:
Avoiding SQL injection
~~~~~~~~~~~~~~~~~~~~~~
Since a ``Func``'s keyword arguments for ``__init__()`` (``**extra``) and
``as_sql()`` (``**extra_context``) are interpolated into the SQL string rather
than passed as query parameters (where the database driver would escape them),
they must not contain untrusted user input.
For example, if ``substring`` is user-provided, this function is vulnerable to
SQL injection::
from django.db.models import Func
class Position(Func):
function = 'POSITION'
template = "%(function)s('%(substring)s' in %(expressions)s)"
def __init__(self, expression, substring):
# substring=substring is a SQL injection vulnerability!
super().__init__(expression, substring=substring)
This function generates a SQL string without any parameters. Since ``substring``
is passed to ``super().__init__()`` as a keyword argument, it's interpolated
into the SQL string before the query is sent to the database.
Here's a corrected rewrite::
class Position(Func):
function = 'POSITION'
arg_joiner = ' IN '
def __init__(self, expression, substring):
super().__init__(substring, expression)
With ``substring`` instead passed as a positional argument, it'll be passed as
a parameter in the database query.
Adding support in third-party database backends
-----------------------------------------------