Являются ли mysql_real_escape_string () и mysql_escape_string () достаточными для безопасности приложения?

Будет ли mysql_real_rescape_string () достаточно, чтобы защитить меня от хакеров и SQL-атак? Спросить, потому что я слышал, что они не помогают против всех атак? Ищем совет экспертов.

EDIT: Также, как насчет LIKE SQL-атак?

@Charles крайне правильно!

Вы подвергаете себя риску для нескольких типов известных SQL-атак, в том числе, как вы упомянули

  • SQL-инъекция: да! Mysql_Escape_String, вероятно, STILL позволяет вам подвергать SQL-инъекциям, в зависимости от того, где вы используете PHP-переменные в своих запросах.

Учти это:

 $sql = "SELECT number FROM PhoneNumbers " . "WHERE " . mysql_real_escape_string($field) . " = " . mysql_real_escape_string($value); 

Может ли это быть безопасно и точно сбежать таким образом? НЕТ! Зачем? потому что хакер может очень хорошо это сделать:

Повторяй за мной:

mysql_real_escape_string() предназначен только для mysql_real_escape_string() переменных данных, НЕ имен таблиц, имен столбцов и, особенно, не полей LIMIT.

  • LIKE exploits: LIKE «$ data%», где $ data могут быть «%», которые возвращают ВСЕ записи … которые вполне могут быть уязвимостью безопасности … просто представьте себе поиск по последним четырем цифрам кредитной карты .. ООП! Теперь хакеры могут получить каждый номер кредитной карты в вашей системе! (BTW: Хранение полных кредитных карт вряд ли когда-либо рекомендуется!)

  • Charset Exploits: Независимо от того, что говорят ненавистники, Internet Explorer по- прежнему в 2011 году уязвим для функций набора символов, и это верно, если вы правильно разработали свою HTML-страницу, с эквивалентом <meta name="charset" value="UTF-8"/> ! Эти атаки ОЧЕНЬ неприятны, поскольку они дают хакеру такой же контроль, как прямые инъекции SQL: например, полный.

Вот пример кода, чтобы продемонстрировать все это:

 // Contains class DBConfig; database information. require_once('../.dbcreds'); $dblink = mysql_connect(DBConfig::$host, DBConfig::$user, DBConfig::$pass); mysql_select_db(DBConfig::$db); //print_r($argv); $sql = sprintf("SELECT url FROM GrabbedURLs WHERE %s LIKE '%s%%' LIMIT %s", mysql_real_escape_string($argv[1]), mysql_real_escape_string($argv[2]), mysql_real_escape_string($argv[3])); echo "SQL: $sql\n"; $qq = mysql_query($sql); while (($data = mysql_fetch_array($qq))) { print_r($data); } 

Вот результаты этого кода при передаче различных входных данных:

 $ php sql_exploits.php url http://www.reddit.com id SQL generated: SELECT url FROM GrabbedURLs WHERE url LIKE 'http://www.reddit.com%' ORDER BY id; Returns: Just URLs beginning w/ "http://www.reddit.com" $ php sql_exploits.php url % id SQL generated: SELECT url FROM GrabbedURLs WHERE url LIKE '%%' ORDER BY id; Results: Returns every result Not what you programmed, ergo an exploit -- 

$ php sql_exploits.php 1 = 1 ' http://www.reddit.com ' id Результаты: возвращает каждый столбец и каждый результат.

Тогда есть ДЕЙСТВИТЕЛЬНО неприятные эксплоиты LIMIT:

 $ php sql_exploits.php url > 'http://www.reddit.com' > "UNION SELECT name FROM CachedDomains" Generated SQL: SELECT url FROM GrabbedURLs WHERE url LIKE 'http://reddit.com%' LIMIT 1 UNION SELECT name FROM CachedDomains; Returns: An entirely unexpected, potentially (probably) unauthorized query from another, completely different table. 

Понимаете ли вы, что SQL в атаках или нет, он неуязвим. Это продемонстрировало, что mysql_real_escape_string () легко обойти даже самые незрелые хакеры. Это потому, что это РЕАКТИВНЫЙ защитный мехизм. Он только фиксирует очень ограниченные и KNOWN эксплойты в базе данных.

Все экранирование никогда не будет достаточным для защиты баз данных. На самом деле, вы можете явно РЕАКТАТЬ для каждого KNOWN-эксплойта и в будущем, ваш код, скорее всего, станет уязвимым для атак, обнаруженных в будущем.

Правильная и единственная (действительно) защита – это PROACTIVE: используйте подготовленные заявления. Подготовленные заявления разрабатываются с особой тщательностью, поэтому выполняется ТОЛЬКО действительный и ПРОГРАММИРОВАННЫЙ SQL. Это означает, что при правильном выполнении шансы неожиданного SQL, которые могут быть выполнены, резко сокращаются.

Теоретически, готовые операторы, которые реализованы отлично, будут непроницаемы для ВСЕХ атак, известных и неизвестных, поскольку они являются техникой SERVER SIDE, обрабатываемой серверами DATABASE SERVERS THEMSELVES и библиотеками, которые взаимодействуют с языком программирования. Таким образом, вы ВСЕГДА гарантируете, что будете защищены от КАЖДОГО ИЗВЕСТНОГО ХАЗА, на минимальном минимуме.

И это меньше кода:

 $pdo = new PDO($dsn); $column = 'url'; $value = 'http://www.stackoverflow.com/'; $limit = 1; $validColumns = array('url', 'last_fetched'); // Make sure to validate whether $column is a valid search parameter. // Default to 'id' if it's an invalid column. if (!in_array($column, $validColumns) { $column = 'id'; } $statement = $pdo->prepare('SELECT url FROM GrabbedURLs ' . 'WHERE ' . $column . '=? ' . 'LIMIT ' . intval($limit)); $statement->execute(array($value)); while (($data = $statement->fetch())) { } 

Теперь это было не так сложно? И это на сорок семь процентов меньше кода (195 символов (PDO) против 375 символов (mysql_). Это то, что я называю «полным выигрыша».

EDIT: Чтобы ответить на все противоречия, этот ответ взволнован, позвольте мне повторить то, что я уже сказал:

Использование подготовленных операторов позволяет использовать защитные меры самого SQL-сервера, и поэтому вы защищены от того, о чем знают люди SQL-сервера. Из-за этого дополнительного уровня защиты вы намного безопаснее, чем просто используя экранирование, независимо от того, насколько тщательны.

Нет!


Важное обновление: после тестирования возможного кода эксплойта, предоставленного Col. Shrapnel и просмотра версий MySQL 5.0.22, 5.0.45, 5.0.77 и 5.1.48, кажется, что набор символов GBK и, возможно, другие в сочетании с версией MySQL ниже чем 5.0.77, может оставить ваш код уязвимым, если вы используете только SET NAMES вместо использования определенных mysql_set_charset / mysqli_set_charset . Поскольку они были добавлены только в PHP 5.2.x, комбинация старого PHP и старого MySQL может дать потенциальную уязвимость в SQL-инъекции , даже если вы считаете, что находитесь в безопасности и все сделали правильно, по-книжной.


Не устанавливая набор символов в сочетании с mysql_real_escape_string , вы можете оказаться уязвимым для использования определенного набора символов с более старыми версиями MySQL. Дополнительная информация о предыдущих исследованиях .

Если возможно, используйте mysql_set_charset . SET NAMES ... недостаточно для защиты от этого конкретного эксплойта, если вы используете исполняемую версию MySQL (до 5.0.22 5.0.77).

Да. Если вы не забудете:

  1. mysql_real_rescape_string() строковые данные с помощью mysql_real_rescape_string()
  2. Прямое число чисел в числах (т.е.: $id = (int)$_GET['id']; )

то вы защищены.

Я лично предпочитаю подготовленные заявления :

 <?php $stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?"); if ($stmt->execute(array($_GET['name']))) { while ($row = $stmt->fetch()) { print_r($row); } } ?> 

Было бы довольно легко упустить ту или иную конкретную переменную, которая была пропущена при использовании одной из *escape_string() , но если все ваши запросы являются подготовленными операторами, то все они в порядке, и использование интерполированных переменных будет выделяться как больной палец.

Но этого недостаточно, чтобы убедиться, что вы не уязвимы для удаленных эксплойтов: если вы передаете команду &admin=1 с запросами GET или POST чтобы указать, что кто-то является администратором, каждый из ваших пользователей может легко обновить свои привилегии с двумя или тремя секундами усилий. Обратите внимание, что эта проблема не всегда очевидна :), но это простой способ объяснить последствия доверяющего пользователю ввода слишком много.

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