SQL инъекция — это стандартная уязвимость, в которой атакующая сторона изменяет параметры веб страницы (такие как GET/POST данный или URL) для внедрения определённых кусков SQL запроса, которые наивное веб приложение выполняет напрямую над своей базой данных. Вероятно, это наиболее опасная и, к сожалению, наиболее часто встречающаяся уязвимость.
Данная уязвимость чаще всего проявляется, когда SQL собирается вручную на основе пользовательского ввода. Например, представим создание функции для получения списка с контактной информацией с помощью страницы поиска. Для защиты данных об адресах электронной почты от спаммеров, мы будем требовать от пользователя ввода чьего-нибудь имени и будет предоставлять адрес для введённого имени:
def user_contacts(request):
user = request.GET['username']
sql = "SELECT * FROM user_contacts WHERE username = '%s';" % username
# execute the SQL here...
Замечание
В данном примере, и в других подобных «не делай так» примерах, мы намеренно опустим большую часть кода, необходимого для работоспособности описываемых функций. Мы не желаем, чтобы кто-нибудь использовал такой код вне контекста данной главы.
И хотя такой код не выглядит опасным, он таковым является.
Во-первых, наша попытка защитить весь наш список адресов электронной почты провалится из-за хитро составленного запроса. Подумайте о том, что может случиться, если атакующий введёт ' OR 'a'='a в поле запроса. В этом случае, будет сконструирован следующий запрос:
SELECT * FROM user_contacts WHERE username = '' OR 'a' = 'a';
Так как мы не проверяем введённую строку на наличие SQL команд, атакующий добавил выражение OR и это приведёт к выдаче всех записей из соответствующей таблицы.
Однако, существует как минимум ещё одна опасность. Представьте, что может случиться, если атакующий введёт '; DELETE FROM user_contacts WHERE 'a'='a'. Вот так будет выглядеть сконструированный запрос:
SELECT * FROM user_contacts WHERE username = '';
DELETE FROM user_contacts WHERE 'a' = 'a';
Ой! А куда делся наш список контактов?
Несмотря на то, что данная проблема является неочевидной, решение для неё будет простым: никогда не доверяйте пользовательским данным и всегда экранируйте всё, что вы используете при конструировании SQL запроса.
Всё это Django делает на уровне API для работы с базой данных. Он автоматически экранирует все специальные SQL параметры, учитывая соглашение об использовании кавычек для используемого вами сервера баз данных (т.е., PostgreSQL или MySQL).
Например, в данном вызове API:
foo.get_list(bar__exact="' OR 1=1")
Django выполнит соответствующее экранирование и результирующий оператор будет выглядеть так:
SELECT * FROM foos WHERE bar = '\' OR 1=1'
т.е., очень безобидно.
Это поведение характерно для всего API с некоторыми исключениями:
Аргумент where у метода
extra()(см. приложение «Справочник по API взаимодействия с базой данных»). Этот параметр принимает сырой SQL, так было заложено при его разработке).Вручную созданные запросы с помощью API низкого уровня.
В каждом из этих случаев, несложно защитить себя от ошибок. Надо просто использовать привязку переменных вместо конструирования запросов. Таким образом, предыдущий пример должен быть переписан так:
from django.db import connection
def user_contacts(request):
user = request.GET['username']
sql = "SELECT * FROM user_contacts WHERE username = %s;"
cursor = connection.cursor()
cursor.execute(sql, [user])
# ... do something with the results
Низкоуровневый метод execute() принимает
с SQL строку с символами подстановки (%s) и
автоматически экранирует и подставляет параметры из списка,
переданного вторым аргументом. Вы должны
всегда конструировать свои SQL запросы
таким способом.
К сожалению, вы не можете свободно использовать привязку
переменных. Например, нельзя таким способом определять
идентификаторы (т.е., имя таблицы или полей
таблиц). Следовательно, если вам потребуется динамически
создать список таблиц по, скажем, содержимому переменной
POST, вам придётся экранировать их имена в
своём коде. Django предоставляет функцию,
django.db.backend.quote_name(),
которая осуществляет экранирование идентификатора в
соответствии с принятой схемой квотирования для текущей базы
данных.
| Пред. | Уровень выше | След. |
| Глава 19. Безопасность | Начало | Межсайтовый скриптинг (XSS) |
0 comments | Make a comment