Может кто-то ясно объяснить, почему mysqli_prepare()/bind_param() лучше, чем real_escape_string()?

Хорошо, я все еще не понимаю. Я продолжаю читать, что для того, чтобы правильно избежать ваших запросов MySQL, вы должны использовать mysqli_prepare() а также mysqli_bind_param(),

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

Я думаю, я просто не понимаю, какая разница между:

<?php
$sql = new \MySQLi(...);
$result = $sql->query('
   UPDATE `table`
   SET
      `field` = "'.$sql->real_escape_string($_REQUEST[$field]).'";
');
?>

а также

<?php
$sql = new \MySQLi(...);
$stmt = $sql->prepare('
   UPDATE `table`
   SET
      `field` = ?;
');
$value = $_REQUEST[$field];
$stmt->bind_param('s', $value);
$stmt->execute();
$result = $stmt->get_result();
unset($value);
?>

кроме больше кода.

Я имею в виду, реализовали ли они это так, чтобы люди не забыли экранировать значения перед отправкой их в запросе? Или это как-то быстрее?

Или я должен использовать этот метод, когда я собираюсь использовать один и тот же запрос несколько раз (так как mysqli_stmt можно использовать повторно) и использовать традиционный метод в других случаях?

4 ответа

Решение

Что вы читаете, что вам нужно использовать mysqli_prepare() а также mysqli_bind_param() функции для "правильного экранирования ваших запросов MySQL" неверны.

Это правда, что если вы используете mysqli_prepare() а также mysqli_bind_param() вам не нужно (и не следует) "экранировать" значения, предоставляемые в качестве параметров связывания. В этом смысле в том, что вы читаете, есть доля правды.

Только когда небезопасные переменные включены в текст SQL (фактический текст запроса), вам нужно "правильно экранировать" переменные, обычно путем переноса переменных в mysqli_real_escape_string() вызовы функций.

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

MySQL теперь поддерживает подготовленные операторы "на стороне сервера" (если опция включена в соединении), и это оптимизация производительности (в некоторых случаях) повторного выполнения идентичного текста SQL. (Это давно поддерживается в других базах данных, таких как Oracle, где использование готовых операторов было привычным паттерном, например, всегда).

В: Реализовали ли они [подготовленные операторы], чтобы люди не забыли экранировать значения перед отправкой их в запросе?

A: Основано на количестве примеров кода, уязвимого для SQL-инъекций, когда не используются подготовленные операторы, несмотря на документацию, касающуюся mysql_real_escape_string() функции, вы думаете, что, безусловно, будет достаточной причиной.

Я думаю, что одно большое преимущество заключается в том, что когда мы читаем код, мы можем видеть оператор SQL как однострочный литерал, а не как конкатенацию множества переменных с кавычками и точками и вызовами mysql_real_escape_string, что не так уж и сложно. плохо с простым запросом, но с более сложным запросом, это просто слишком громоздко. Использование ? заполнитель делает для более понятного оператора SQL,... правда, мне нужно взглянуть на другие строки кода, чтобы выяснить, какое значение там заполняется. (Я думаю, что стиль Oracle назвал параметры :fee, :fi, :fo, :fum предпочтительнее позиционного ?, ?, ?, ? примечание.) Но наличие текста STATIC SQL - это то, что действительно является преимуществом.

Q: Или это как-то быстрее?

Как я упоминал ранее, использование подготовленных операторов на стороне сервера может быть преимуществом с точки зрения производительности. Не всегда это происходит быстрее, но при многократном выполнении одного и того же оператора, где единственное отличие - это буквальные значения (как при многократных вставках), это может обеспечить повышение производительности.

Q: Или я должен использовать этот метод, когда я собираюсь использовать один и тот же запрос несколько раз (поскольку mysqli_stmt можно использовать повторно) и использовать традиционный метод в других случаях?

Это зависит от вас. Я предпочитаю использовать текст STATIC SQL. Но это действительно происходит из долгой истории использования Oracle, и использование того же шаблона с MySQL подходит естественно. (Хотя из Perl, использующего интерфейс DBI, и Java, использующий JDBC и MyBATIS, или другие ORM (Hibernate, Glassfish JPA и др.)

Следование тому же шаблону просто кажется естественным в PHP; Введение mysqli_ и PDO - долгожданное облегчение от загадочного (и оскорбленного) интерфейса mysql_.

Хороший код может быть написан по любой схеме. Но я призываю вас подумать о более сложных операторах SQL и о том, стоит ли выбирать mysqli_real_escape_string()и объединение динамической строки, которая должна быть выполнена, вместо использования статического SQL-текста и параметров связывания, может сделать чтение и расшифровку, причем фактический SQL-запрос выполняется более сложным для души, которая обнаруживает, что поддерживает код, который они не писали.

Я думаю, что исследования показали, что код читается в десять раз больше, чем написано, поэтому мы стремимся создавать читаемый, понятный код, даже если это означает больше строк кода. (Когда каждый оператор выполняет одну идентифицируемую вещь, мне, как правило, легче понять, чем читать беспорядочные вызовы сцепленных функций в одном сложном операторе.

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

Я нашел несколько основных преимуществ, которые были хорошо написаны:

  1. Затраты на компиляцию и оптимизацию оператора возникают только один раз, хотя оператор выполняется несколько раз. Не вся оптимизация может быть выполнена во время компиляции подготовленного оператора по двум причинам: лучший план может зависеть от конкретных значений параметров, а лучший план может изменяться по мере изменения таблиц и индексов с течением времени.
  2. Подготовленные операторы устойчивы к внедрению SQL, потому что значения параметров, которые передаются позже с использованием другого протокола, не должны быть правильно экранированы. Если исходный шаблон оператора не является производным от внешнего ввода, внедрение SQL невозможно.

С другой стороны, если запрос выполняется только один раз, подготовленные операторы на стороне сервера могут быть медленнее из-за дополнительной обратной передачи на сервер. Ограничения реализации также могут привести к снижению производительности: некоторые версии MySQL не кэшируют результаты подготовленных запросов, а некоторые СУБД, такие как PostgreSQL, не выполняют дополнительную оптимизацию запросов во время выполнения.

Источник

Я хотел бы добавить, что mysqli_bind_param() был удален с PHP 5.4.0. Вы должны использовать mysqli_stmt_bind_param()

Другие вопросы по тегам