разница в безопасности между (double) $ user_input и bind_param ('d', $ user_input)

Предположим, что я должен был выполнить подготовленное заявление следующим образом:

$qry->prepare('UPDATE table_name SET column1 = ? string_column = ? WHERE column3 = ? AND column4 = ?'); $qry->bind_param('sbid', $string, $blob, $int, $double); $int = 'non int value'; /* gives 0 in the database */ $blob = 'some string'; $string = 'another string'; $double = $double; $qry->execute(); $qry->close(); 

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

 $int = (int) $int; $blob = "'" .mysql_real_escape_string($blob) ."'"; $string = "'" .mysql_real_escape_string($blob) ."'"; $double = (double) $double; $db->query("UPDATE SET column1 = $int, column2 = $blob WHERE column3 = $string AND column4 = $double "); 

PS. Мне неинтересно, как подготовленные утверждения улучшают производительность, но о безопасности и разнице скоростей для одного запроса.

Этого очень много. Некоторые случайные точки

  • Подготовленные заявления одного использования действительно налагают (более чем теоретические) штрафные санкции, что выше, если на сервере MySQL существует множество соединений. (Подумайте: переключатели контекста)
  • Но вы не должны запускать сервер БД так близко к его границам, что это имеет значение.
  • Но у вас не всегда есть выбор (Think: shared hosting)

или:

  • Есть некоторые (или даже многие) случаи, когда подготовленные заявления не предлагают преимущества безопасности – существует много бизнес-логики, в которой не задействованы данные, созданные пользователем (Think: Jointables, которые содержат только идентификаторы) или где пользователь -генерированные данные должны быть проверены заранее по другим причинам (подумайте: расчеты цены, поиск memcached, …)
  • Но выбор одного из многих стилей для каждого отдельного запроса приводит к недостижимому коду.
  • Но иногда это неизбежно (подумайте: нет поддержки запросов для конструкции IN ( ) )

часто упускается из виду:

  • Подготовленные запросы иногда усложняют RDBMS-агностик
  • Но подготовленные запросы предлагают лучшую защиту от SQL-инъекций.

Мой любимый:

  • это общий совет просто всегда использовать подготовленные запросы
  • Но большинство живых существ на этой планете посоветует вам есть фекалии или гниющие органические вещества.

Поэтому выбор стиля часто приходится делать в каждом конкретном случае. Мы использовали способ инкапсуляции всего доступа к БД, включая управление параметрами, в стандартизованную библиотеку, которая просто require() ed, поэтому вы можете вставлять замену с помощью подготовленных запросов, экранов или любого другого, что вы хотите, и ваша RDBMS поддерживает.

Спасибо за большой вопрос.

По сути, вы можете использовать оба метода одновременно.

Большинство людей путают идею подготовленного заявления в целом с [очень ограниченной] реализацией, предлагаемой крупными СУБД. В то время как последнее может быть поставлено под сомнение, первое – это единственный способ.

Взгляните на пример. Давайте запустим ваш запрос с помощью safeMysql :

 $sql = "UPDATE SET column1 = ?i, column2 = ?s WHERE column3 = ?s AND column4 = ?s"; $db->query($sql, $string, $blob, $int, $double); 

Он выполняет очень строковое форматирование, как ваш код, но делает его внутренне . Зачем? Потому что неважно, как это реализовано внутренне (с помощью встроенного подготовленного оператора или ручного форматирования), но важно использовать подготовленный оператор для сборки вашего запроса в любом случае.

Есть некоторые существенные моменты в отношении подготовленных заявлений, которые игнорируются большинством людей:

  1. он делает форматирование всегда полным (хотя в вашем примере вы делаете правильные вещи и делаете полное форматирование, все равно очень легко перейти к неполному, например:

     $colname = "`$colname`"; 
  2. ваше форматирование всегда правильное. он не позволит вам сделать что-то вроде

     $colname = "`" .mysql_real_escape_string($colname) ."`"; 

    который будет бесполезен и приведет вас к инъекции

  3. он сделает форматирование обязательным . С сборкой запроса ваш текущий способ очень легко упустить переменную или две.

  4. он будет делать правильное форматирование как можно ближе к выполнению запроса. Это очень важно, поскольку
    • он не испортит вашу исходную переменную (что, если запрос не удался, и вы хотите повторить его обратно?)
    • он не позволит вам переместить код форматирования где-нибудь в стороне от запроса, что может привести к фатальным последствиям.
    • в конце концов, это сделает ваш код значительно короче, без всего этого скучного ручного форматирования!

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

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

Помня все, что нужно сделать, нужно реализовать саму идею подготовленного заявления в своей библиотеке доступа к БД, чтобы сделать код безопасным и коротким.

Всего несколько примеров из safeMysql:

 $name = $db->getOne('SELECT name FROM table WHERE id = ?i',$_GET['id']); $data = $db->getInd('id','SELECT * FROM ?n WHERE id IN ?a','table', array(1,2)); $data = $db->getAll("SELECT * FROM ?n WHERE mod=?s LIMIT ?i",$table,$mod,$limit); $ids = $db->getCol("SELECT id FROM tags WHERE tagname = ?s",$tag); $data = $db->getAll("SELECT * FROM table WHERE category IN (?a)",$ids); $data = array('offers_in' => $in, 'offers_out' => $out); $sql = "INSERT INTO stats SET pid=?i,dt=CURDATE(),?u ON DUPLICATE KEY UPDATE ?u"; $db->query($sql,$pid,$data,$data); 

Просто попробуйте то же самое с обычным mysql (i) и посмотрите, сколько кода вам потребуется.

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

Я считаю, что они одинаково безопасны с точки зрения безопасности, но использование prepare не только делает ваш SQL безопасным, но и делает вас безопасным. Вы не можете доверять себе, чтобы вручную бежать и преобразовывать в правильный тип все время. Если вы напишете 10 000 различных SQL-запросов, вы, как правило, забудете избежать одного или двух.

Итак, в заключение, prepare – лучшая привычка бороться с инъекцией SQL. Помещение PHP-переменной непосредственно в SQL-запрос заставляет меня чувствовать себя неловко, когда спать по ночам.