Должны ли мы всегда связывать наши операторы 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();
Связывая идентификатор как число, а Title - строку, мы можем ограничить ущерб, нанесенный, когда кто-то пытается выполнить SQL-инъекцию в коде.
Должны ли мы всегда связывать наши значения с PDO::PARAM_
, поэтому мы можем ограничить то, что можно извлечь из базы данных в SQL-инъекции? Означает ли это повышение безопасности с помощью PDO при выполнении нашего bindValue()
?
Ответы
Ответ 1
В одном есть два вопроса. Важно не путать их.
- Должны ли мы всегда использовать placeholder для представления переменных в запросе?
- Должны ли мы всегда использовать определенную функцию в коде приложения, чтобы следовать приведенному выше правилу?
Кроме того, из пояснений в комментариях в начале сообщения, можно увидеть третий вопрос:
- Должен ли мы всегда использовать третий параметр, или это ОК, чтобы PDO связывает все параметры как строки по умолчанию?
1. Для первого вопроса ответ абсолютно и определенно - ДА.
Пока для второго, ради здравого смысла кода и СУШКИ -
2. Избегайте ручного связывания, если это возможно.
Существует много способов избежать ручной привязки. Некоторые из них:
-
ORM - отличное решение для простых операций CRUD и должно иметь в современном приложении. Он полностью скроет SQL от вас, выполнив привязку за кулисами:
$user = User::model()->findByPk($id);
-
Query Builder - это также способ, замаскировавший 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()
без каких-либо проблем.
Ответ 2
Вы должны обязательно использовать API prepare
и передавать значения отдельно от запроса, в отличие от простой интерполяции строк (например, "SELECT * FROM foo WHERE bar = '$baz'"
→ bad).
Для параметров привязки у вас есть три варианта:
Неважно, какие из них вы используете, все они одинаково безопасны. См. Ответы на некоторые подробности о различиях:
При использовании bindParam
или bindValue
передача третьего аргумента типа PDO::PARAM_
необязательна. Если вы его не передадите, по умолчанию используется привязка аргумента как строки. Это означает, что вы можете получить запрос, эквивалентный ... WHERE foo = '42'
вместо ... WHERE foo = 42
. Это зависит от вашей базы данных, как она справится с этим. MySQL будет автоматически вводить строку в число, как это делает PHP (например, в '42' + 1
). Другие базы данных могут быть более суетливыми в отношении типов.
Опять же, все варианты одинаково безопасны. Если вы пытаетесь связать строку 'foo'
с помощью PDO::PARAM_INT
, строка будет отлита к целому числу и, соответственно, привязана как значение 0
. Нет возможности для инъекций.
Ответ 3
Да, вы всегда должны связывать params с подготовленным оператором.
Это более безопасно и ограничивает SQL-инъекцию. Но это не единственное, что вам нужно сделать для запроса параметров: необходим правильный тип управления, лучше всего, если вы сопоставляете строку с объектом и генерируете в ней исключение, если оно имеет недопустимые данные.
Надеюсь, я могу быть полезным!
Ответ 4
Да, привязка - путь. Или параметризованные запросы, которые имеют более обобщенный термин.
@Theo делает замечательную работу, объясняя, почему параметризованные запросы - это путь
Вы также можете использовать хранимые процедуры для дополнительной безопасности, но это более убитое, если у вас есть одно приложение в одной базе данных. Он хорош для нескольких приложений в одной базе данных, чтобы обеспечить согласованность при обработке данных.