Будет ли mysql_real_rescape_string () достаточно, чтобы защитить меня от хакеров и SQL-атак? Спросить, потому что я слышал, что они не помогают против всех атак? Ищем совет экспертов.
EDIT: Также, как насчет LIKE SQL-атак?
@Charles крайне правильно!
Вы подвергаете себя риску для нескольких типов известных SQL-атак, в том числе, как вы упомянули
Учти это:
$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).
Да. Если вы не забудете:
mysql_real_rescape_string()
строковые данные с помощью mysql_real_rescape_string()
$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
чтобы указать, что кто-то является администратором, каждый из ваших пользователей может легко обновить свои привилегии с двумя или тремя секундами усилий. Обратите внимание, что эта проблема не всегда очевидна :), но это простой способ объяснить последствия доверяющего пользователю ввода слишком много.
Вместо этого вы должны изучить использование подготовленных операторов / параметризованных запросов. Идея состоит в том, что вы даете базе данных запрос с заполнителями. Затем вы даете базе данных свои данные и указываете, какой заменитель заменить на указанные данные, и база данных гарантирует, что она действительна и не позволяет ей переполнять местозаполнитель (то есть он не может завершить текущий запрос, а затем добавить его собственная – обычная атака).