Должны ли мы всегда связывать наши операторы SQL?
Я изучал PDO bindValue()
. Я знаю, что подготовка моих SQL-инструкций с помощью PDO предотвращает внедрение SQL-инъекций.
Пример кода:
$stmt = $dbh->prepare('SELECT * FROM articles WHERE id = :id AND title = :title');
$stmt->bindValue(':id', PDO::PARAM_INT);
$stmt->bindValue(':title', PDO::PARAM_STR);
$stmt->execute();
Привязав идентификатор в виде числа, а заголовок был строкой, мы можем ограничить ущерб, наносимый, когда кто-то пытается выполнить SQL-инъекцию в коде.
Должны ли мы всегда связывать наши значения с PDO::PARAM_
, чтобы мы могли ограничить то, что может быть извлечено из базы данных при внедрении SQL? Добавляет ли это больше безопасность с помощью PDO при выполнении наших bindValue()
?
4 answers
В одном есть два вопроса. Важно не путать их
- Должны ли мы всегда использовать заполнитель для представления переменных данных в запросе?
- Должны ли мы всегда использовать определенную функцию в коде приложения , чтобы следовать приведенному выше правилу?
Кроме того, из разъяснений в комментариях под вступительным сообщением можно увидеть третий вопрос: - Должны ли мы всегда использовать третий параметр, или можно разрешить PDO связывать все параметры по умолчанию в виде строк?
1. На первый вопрос ответ абсолютно и определенно - ДА.
В то время как для второго, ради здравомыслия и сухости кода -
2. По возможности избегайте привязки вручную.
Существует множество способов избежать привязки вручную. Некоторые из них:
-
ORM - отличное решение для простых операций с CRUD и должно быть в современном приложении. Это полностью скроет SQL от вас, выполнение привязки за кулисами:
$user = User::model()->findByPk($id);
-
Конструктор запросов также является подходящим вариантом, маскируя SQL в некоторых операторах PHP, но снова скрывая привязку за кулисами:
$user = $db->select('*')->from('users')->where('id = ?', $id)->fetch();
-
Некоторая библиотека абстракций может обрабатывать переданные данные с помощью заполнителей с намеком на тип , снова скрывая фактическую привязку:
$user = $db->getRow("SELECT * FROM users WHERE id =?i", $id);
-
Если вы все еще используете PHP способами прошлого века и у вас есть необработанный PDO по всему коду - тогда вы можете передайте свои переменные в execute(), все еще экономя много времени на ввод:
$stmt = $dbh->prepare('SELECT * FROM users WHERE id = ?'); $stmt->execute([$id]); $user = $stmt->fetch();
Что касается третьего вопроса - до тех пор, пока вы связываете числа в виде строк (но не наоборот!) -
3. С mysql все в порядке, отправлять почти каждый параметр в виде строки
Поскольку mysql всегда преобразует ваши данные в нужный тип. Единственный известный мне случай - это предложение LIMIT, в котором вы не можете форматировать число в виде строки - таким образом, единственным связанным случаем является один , когда PDO устанавливается в режиме эмуляции, и вы должны передать параметр в предложении LIMIT. Во всех остальных случаях вы можете опустить третий параметр, а также явный вызов bindValue()
без каких-либо проблем.
Вам определенно следует использовать API prepare
и передавать значения отдельно от запроса, в отличие от простой интерполяции строк (например "SELECT * FROM foo WHERE bar = '$baz'"
→ плохой).
Для параметров привязки у вас есть три варианта:
На самом деле не имеет значения, какой из них вы используете, все они одинаково безопасны. Смотрите эти ответы для получения некоторых подробностей о различия:
При использовании bindParam
или bindValue
передача третьего типа аргумента PDO::PARAM_
необязательна. Если вы не передадите его, он по умолчанию свяжет аргумент в виде строки. Это означает, что вы можете получить запрос, эквивалентный ... WHERE foo = '42'
вместо ... WHERE foo = 42
. Это зависит от вашей базы данных, как она будет с этим справляться. MySQL автоматически преобразует строку в число как необходим, как и PHP (например, в '42' + 1
). Другие базы данных могут быть более требовательны к типам.
Опять же, все варианты одинаково безопасны. Если вы пытаетесь связать строку 'foo'
с помощью PDO::PARAM_INT
, строка будет преобразована в целое число и, соответственно, связана как значение 0
. Нет никакой возможности для инъекции.
Да, вы всегда должны связывать параметры с подготовленным оператором. Это более безопасно и ограничивает внедрение SQL. Но это не единственное, что вы должны сделать для запроса параметров: требуется правильный элемент управления типом, лучше всего, если вы сопоставите строку с объектом и создадите в нем исключение, если в нем есть недопустимые данные.
Надеюсь, я смогу быть полезен!
Да, привязка - это правильный путь. Или параметризованные запросы, которые являются более обобщенным термином.
Вы также можете использовать хранимые процедуры для дополнительной безопасности, но это перебор, если у вас есть одно приложение к одной базе данных. Это хорошо для нескольких приложений в одной базе данных, чтобы обеспечить согласованность при обработке данных