Должны ли мы всегда связывать наши операторы 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()?

 14
Author: Traven, 2014-05-07

4 answers

В одном есть два вопроса. Важно не путать их

  1. Должны ли мы всегда использовать заполнитель для представления переменных данных в запросе?
  2. Должны ли мы всегда использовать определенную функцию в коде приложения , чтобы следовать приведенному выше правилу?
    Кроме того, из разъяснений в комментариях под вступительным сообщением можно увидеть третий вопрос:
  3. Должны ли мы всегда использовать третий параметр, или можно разрешить 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() без каких-либо проблем.

 10
Author: Your Common Sense, 2017-05-23 11:46:08

Вам определенно следует использовать 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. Нет никакой возможности для инъекции.

 10
Author: deceze, 2017-05-23 11:46:08

Да, вы всегда должны связывать параметры с подготовленным оператором. Это более безопасно и ограничивает внедрение SQL. Но это не единственное, что вы должны сделать для запроса параметров: требуется правильный элемент управления типом, лучше всего, если вы сопоставите строку с объектом и создадите в нем исключение, если в нем есть недопустимые данные.

Надеюсь, я смогу быть полезен!

 2
Author: gafreax, 2014-05-07 07:31:03

Да, привязка - это правильный путь. Или параметризованные запросы, которые являются более обобщенным термином.

@ Тео проделывает замечательную работу, объясняя, почему параметризованные запросы - это правильный путь

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

 1
Author: Krimson, 2017-05-23 12:00:10