Простой случай внедрения SQL работает следующим образом (в псевдокоде):
name = form_params["name"]
year = 2011
sql = "INSERT INTO Students (name, year) " +
"VALUES ('" + name + "', " + year + ");"
database_handle.query(sql)
year
предоставлен вами, программистом, поэтому он не испорчен и может быть встроен в запрос влюбым подходящим способом;в этом случае - как число без кавычек.
Но name
предоставляется пользователем и может быть любым.Приходит Таблицы Бобби и вводит это значение:
name = "Robert'); DROP TABLE Students; -- "
И запрос становится
INSERT INTO Students (name, year) VALUES ('Robert');
DROP TABLE Students; -- ', 2011);
Эта подстановка превратила один ваш запрос в два.
Первый выдает ошибку из-за несоответствующего количества строк, но это не имеет значения, потому что база данных может однозначно найти и выполнить второй запрос.Злоумышленник может обойти ошибку, в любом случае возиться с вводом.--
является комментарием, так что остальная часть ввода игнорируется.
Обратите внимание, как данные внезапно стали кодом - типичный признак проблемы безопасности.
Что предлагает предложенная замена, так этоthis:
name = form_params["name"].regex_replace("'", "\\\\'")
Как это работает, сбивает с толку, поэтому мой предыдущий комментарий.Строковый литерал "\\\\'"
представляет строку \\'
.Функция regex_replace
интерпретирует это как строку \'
.Затем база данных видит
... VALUES ('Robert\'); DROP TABLE Students; -- ', 2011);
и правильно интерпретирует это как довольно необычное имя.
Среди других проблем этот подход очень хрупок.Если строки, которые вы используете на своем языке, не заменяют \\
на \
, если ваша функция подстановки строк не интерпретирует \\
как \
(если это не функция регулярного выражения или вместо нее используется $1
\1
для обратных ссылок) вы можете получить четное количество слешей, таких как
... VALUES ('Robert\\'); DROP TABLE Students; -- ', 2011);
и no SQL-инъекция будет предотвращена.
Решениене для проверки того, что делает язык и библиотека со всеми возможными входными данными, которые вы можете придумать, или для предвидения того, что они могут сделать в будущей версии, а скорее для использования возможностей, предоставляемых базой данных.Обычно они бывают двух видов:
экранирование с учетом базы данных, которое обеспечивает точное экранирование любых данных, поскольку клиентская библиотека соответствует серверу и знает, какую кодировку символов использует база данных.вы запрашиваете:
sql = "... '" + database_handle.escape(name) + "' ..."
внеполосное представление данных (обычно с подготовленными отчетами), поэтому данные даже не совпадаютстрока как код:
sql = "... VALUES (:n, :y);"
database_handle.query(sql, n = name, y = year)