Будет ли mysql_real_rescape_string() достаточно, чтобы защитить меня от хакеров и SQL-атак? Спросить, потому что я слышал, что они не помогают против всех атак? Ищите советы экспертов.
EDIT: Кроме того, как насчет LIKE SQL-атак?
Будет ли 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()
предназначен только для исключения переменных данных, НЕ имен таблиц, имен столбцов и, особенно, не полей 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 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_). То, что я называю "полным выигрыша".
РЕДАКТИРОВАТЬ: Чтобы решить все противоречия, этот ответ взволнован, позвольте мне повторить то, что я уже сказал:
Использование подготовленных инструкций позволяет использовать защитные меры сам SQL-сервер и, следовательно, вы защищены от того, что Об этом знают люди SQL-сервера. Потому как этого дополнительного уровня защиты вы гораздо безопаснее, чем просто используя избегая, как бы тщательны.
Важное обновление: После тестирования возможного кода эксплоита, предоставленного Col.Sprapnel, и просмотра версий 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()
$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
, чтобы указать, что кто-то является администратором, каждый из ваши пользователи могут легко повысить свои привилегии с помощью двух или трех секунд усилий. Обратите внимание, что эта проблема не всегда очевидна:), но это простой способ объяснить последствия чрезмерного ввода запросов пользователя.
Вместо этого вы должны изучить использование подготовленных операторов/параметризованных запросов. Идея состоит в том, что вы даете базе данных запрос с заполнителями. Затем вы предоставляете базе данных свои данные и указываете, какой заменитель заменить на указанные данные, и база данных гарантирует, что она действительна и не позволяет ей переполнять местозаполнитель (то есть он не может завершить текущий запрос, а затем добавить его собственная - обычная атака).