У меня есть форма, в которую посетитель может ввести данные, и я хочу сохранить эти данные в базе данных mysql с помощью переменной $ _POST. Что мне нужно для предотвращения внедрения SQL?
Используйте подготовленные заявления .
Я представил презентацию на конференции TEK-X в мае 2010 года по этому вопросу, и я попытался охватить несколько методов защиты от SQL Injection. Во всех случаях нет единого метода, поэтому вы должны изучить несколько методов и использовать их все:
Проверяйте ввод пользователя или любой другой контент из внешних источников (даже данных из вашей собственной базы данных) перед его интерполяцией в SQL-запрос. Например, вы можете использовать расширение фильтра PHP или регулярные выражения.
Принудительное внешнее содержимое должно быть в правильном формате. Например, (int) $_POST["userid"]
указывает, что контент является простым целым числом, поэтому его можно использовать безопасно.
При включении динамического содержимого вместо буквенных значений в выражениях SQL используйте подготовленные запросы с параметрами. Обратите внимание, что обычное расширение mysql
в PHP не поддерживает параметры запроса – используйте PDO . Я не использую mysqli
потому что его API является непоследовательным и сложным в использовании.
При использовании предиката IN()
вы не можете использовать один параметр для списка значений. Объединение нескольких заполнителей параметров, столько, сколько у вас есть значений в вашем списке. Это не сложно, это всего лишь строка или два кода:
$sql = "SELECT ... FROM ... WHERE user_id IN (" . join(",", array_fill(0,count($userid_list),"?")) . ")"; $pdoStmt = $pdo->prepare($sql); $pdoStmt->execute($userid_list);
При использовании динамических имен таблиц, имен столбцов или ключевых слов SQL вы не можете использовать параметры запроса. Вы должны интерполировать динамический контент. Но вы можете использовать методы «белого списка» для сопоставления ненадежного контента с юридическими, безопасными идентификаторами и ключевыми словами.
Для получения дополнительной информации и примеров см. Мою презентацию « Мифы о выбросах SQL» и «Ошибки» .
Также вам может понравиться моя новая книга SQL Antipatterns: избежание ошибок программирования баз данных . В моей книге есть глава о SQL Injection.
Прочитайте это, и в следующий раз выполните некоторые поиски:
https://stackoverflow.com/questions/1973/what-is-the-best-way-to-avoid-sql-injection-attacks
Вы должны следовать некоторым правилам при добавлении каких-либо данных в запрос, независимо от того, где он пришел – от пользователя или формы или чего-то еще. Правила всегда остаются неизменными.
Чтобы отправить запрос в базу данных, у вас есть 2 варианта:
Создайте запрос обычным способом, чтобы он выглядел точно так же, как SQL-запрос, который вы можете запускать в консоли sql.
Для этого нужно понимать целый набор правил , а не просто «использовать mysql_real_escape_string».
Правила, такие как:
Отправить запрос и данные отдельно .
Это наиболее предпочтительный способ, так как он может быть сокращен до «использования привязки». Все строки, числа и параметры LIMIT могут быть связаны – не беспокойтесь.
Используя этот метод, ваш запрос с заполнителями будет отправлен в базу данных как есть, и связанные данные отправляются в отдельные пакеты, поэтому он не может вмешиваться. Это похоже на разделение кода и данных . Вы отправляете свою программу (сам запрос) отдельно от данных.
Все сказанное выше охватывает только ввод данных. Но иногда нам приходится делать наш запрос еще более динамичным, добавляя операторы или идентификаторы.
В этом случае каждый динамический параметр должен быть жестко закодирован в нашем скрипте и выбран из этого набора.
Например, для динамического упорядочения:
$orders = array("name","price","qty"); $key = array_search($_GET['sort'],$orders)); $orderby = $orders[$key]; $query = "SELECT * FROM `table` ORDER BY $orderby";
или динамический поиск:
$w = array(); $where = ''; if (!empty($_GET['rooms'])) $w[]="rooms='".mesc($_GET['rooms'])."'"; if (!empty($_GET['space'])) $w[]="space='".mesc($_GET['space'])."'"; if (!empty($_GET['max_price'])) $w[]="price < '".mesc($_GET['max_price'])."'"; if (count($w)) $where="WHERE ".implode(' AND ',$w); $query="select * from table $where";
в этом примере мы добавляем к запросу только данные, введенные пользователем, а не имена полей, которые все жестко закодированы в скрипте. Для привязки алгоритм очень похож
И так далее.