Должны ли мы всегда связывать наши операторы 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 делает замечательную работу, объясняя, почему параметризованные запросы - это путь

Вы также можете использовать хранимые процедуры для дополнительной безопасности, но это более убитое, если у вас есть одно приложение в одной базе данных. Он хорош для нескольких приложений в одной базе данных, чтобы обеспечить согласованность при обработке данных.