diff --git a/docs/ref/models/expressions.txt b/docs/ref/models/expressions.txt index affafa331b..764ec8a955 100644 --- a/docs/ref/models/expressions.txt +++ b/docs/ref/models/expressions.txt @@ -654,11 +654,19 @@ should avoid them if possible. .. warning:: - You should be very careful to escape any parameters that the user can - control by using ``params`` in order to protect against :ref:`SQL injection - attacks `. ``params`` is a required argument to - force you to acknowledge that you're not interpolating your SQL with user - provided data. + To protect against `SQL injection attacks + `_, you must escape any + parameters that the user can control by using ``params``. ``params`` is a + required argument to force you to acknowledge that you're not interpolating + your SQL with user-provided data. + + You also must not quote placeholders in the SQL string. This example is + vulnerable to SQL injection because of the quotes around ``%s``:: + + RawSQL("select col from sometable where othercol = '%s'") # unsafe! + + You can read more about how Django's :ref:`SQL injection protection + ` works. Window functions ---------------- diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 017cdcae29..4903d61132 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -1262,8 +1262,15 @@ generated by a ``QuerySet``. You should be very careful whenever you use ``extra()``. Every time you use it, you should escape any parameters that the user can control by using - ``params`` in order to protect against SQL injection attacks . Please - read more about :ref:`SQL injection protection `. + ``params`` in order to protect against SQL injection attacks. + + You also must not quote placeholders in the SQL string. This example is + vulnerable to SQL injection because of the quotes around ``%s``:: + + "select col from sometable where othercol = '%s'" # unsafe! + + You can read more about how Django's :ref:`SQL injection protection + ` works. By definition, these extra lookups may not be portable to different database engines (because you're explicitly writing SQL code) and violate the DRY diff --git a/docs/topics/db/sql.txt b/docs/topics/db/sql.txt index 969026e56e..dc2bcc50b6 100644 --- a/docs/topics/db/sql.txt +++ b/docs/topics/db/sql.txt @@ -210,20 +210,26 @@ argument. .. warning:: - **Do not use string formatting on raw queries!** + **Do not use string formatting on raw queries or quote placeholders in your + SQL strings!** It's tempting to write the above query as:: >>> query = 'SELECT * FROM myapp_person WHERE last_name = %s' % lname >>> Person.objects.raw(query) - **Don't.** + You might also think you should write your query like this (with quotes + around ``%s``):: - Using the ``params`` argument completely protects you from `SQL injection - attacks`__, a common exploit where attackers inject arbitrary SQL into - your database. If you use string interpolation, sooner or later you'll - fall victim to SQL injection. As long as you remember to always use the - ``params`` argument you'll be protected. + >>> query = "SELECT * FROM myapp_person WHERE last_name = '%s'" + + **Don't make either of these mistakes.** + + As discussed in :ref:`sql-injection-protection`, using the ``params`` + argument and leaving the placeholders unquoted protects you from `SQL + injection attacks`__, a common exploit where attackers inject arbitrary + SQL into your database. If you use string interpolation or quote the + placeholder, you're at risk for SQL injection. __ https://en.wikipedia.org/wiki/SQL_injection @@ -257,6 +263,9 @@ For example:: return row +To protect against SQL injection, you must not include quotes around the ``%s`` +placeholders in the SQL string. + Note that if you want to include literal percent signs in the query, you have to double them in the case you are passing parameters::