Давайте рассмотрим упрощенный пример:
$foo = "world";
echo "Hello $foo";
$foo
- это переменная, содержащая строку, а оператор echo «интерполирует» в другую строку.Строка из $foo
будет помещена непосредственно в строку, и вы не сможете увидеть «соединение».Вывод будет Hello world
.
Теперь давайте добавим несколько цитат внутри строки:
echo "Hello '$foo'";
$foo
по-прежнему интерполируется и теряет свою идентичность, но символы '
также являются частью окончательной строки.Вывод будет Hello 'world'
.
В вашем SQL это то, что вы делаете - вы объединяете несколько строк в одну, и в результате получается оператор SQL.Допустим, SQL, который вы хотите получить, выглядит следующим образом:
SELECT * FROM things WHERE thing_name = 'world'
Эти кавычки показывают, как вы анализатор SQL в базе данных , что 'world'
является строкой, а нескажем, имя столбца.
Используя наше определение $foo
из предыдущего, мы можем построить это так:
$sql = "SELECT * FROM things WHERE thing_name = '$foo'";
Нам все еще нужны одинарные кавычки, потому чтоони являются частью SQL, который мы пытаемся создать.
Однако, как уже отмечали другие, именно отсюда и риск «внедрения SQL».Представьте, что злоумышленник может обмануть нас, установив для $foo
значение по своему выбору:
$foo = "world'; DROP TABLE things; --";
Теперь, когда мы строим нашу строку SQL, мы получаем следующее:
SELECT * FROM things WHERE thing_name = 'world'; DROP TABLE things; --'
Упс!
Самая безопасная защита от этого на самом деле предполагает передачу переменной в виде переменной, а не объединение ее в строку.По сути, вы передаете базе данных две вещи: «параметризованный оператор» и «параметры» для использования с ним.Это утверждение может выглядеть следующим образом:
SELECT * FROM things WHERE thing_name = :foo
Обратите внимание, что в отличие от нашей наивной интерполяции, мы не ставим кавычки вокруг заполнителя :foo
.Это связано с тем, что при правильном использовании текст здесь никогда не подставляется.Вместо этого база данных «подготовит» оператор в виде запроса, подобного «выберет все столбцы из таблицы things
на основе значения, которое будет сопоставлено с thing_name
», и «выполнит» его, сказав «соответствует этой переменнойпротив thing_name
".
Теперь, когда мы передаем строку наших атакующих в качестве параметра для :foo
, мы просто получаем запрос, ищущий вещи с этим именем;поскольку предположительно их не будет, мы получим только пустой результат.