Достаточно ли mysql_real_escape_string () и mysql_escape_string () для безопасности приложения? - PullRequest
20 голосов
/ 24 марта 2011

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

РЕДАКТИРОВАТЬ: Кроме того, как насчет LIKE SQL-атак?

Ответы [ 5 ]

14 голосов
/ 25 марта 2011

@ Чарльз чрезвычайно прав!

Вы подвергаете себя риску нескольких типов известных атак 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() предназначен только для экранирования переменных данных, NOT имен таблиц,имена столбцов, и особенно поля LIMIT.

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

  • Эксплуатация Charset: Независимо от того, что говорят ненавистники, 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 () легко обойдется даже самым незрелым хакером.Это потому, что это РЕАКТИВНЫЙ защитный механизм.Это исправляет только очень ограниченные и ИЗВЕСТНЫЕ эксплойты в базе данных.

Все экранирования НИКОГДА не будет достаточно для защиты баз данных.Фактически, вы можете явно РЕАГИРОВАТЬ на каждую ИЗВЕСТНУЮ эксплойт, и в будущем ваш код, скорее всего, станет уязвимым для атак, обнаруженных в будущем.

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

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

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

$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_). Это то, что я называю "полным выигрыша".

РЕДАКТИРОВАТЬ: Для решения всехСпор этот ответ вызвал, позвольте мне повторить то, что я уже сказал:

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

8 голосов
/ 24 марта 2011

Нет!


Важное обновление: После тестирования возможный код эксплойта, предоставленный полковником Шрапнелем и просмотром версий MySQL 5.0.22, 5.0.45, 5.0.77 и 5.1.48, он Похоже, что набор символов GBK и, возможно, другие в сочетании с версией MySQL ниже 5.0.77 могут сделать ваш код уязвимым, если вы only вместо этого используете 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).

7 голосов
/ 24 марта 2011

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

  1. Сбросить строковые данные с помощью mysql_real_rescape_string()
  2. Преобразование чисел в числа явно (т.е.тогда вы защищены.
3 голосов
/ 24 марта 2011

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

<?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 запросами, чтобы показать, что кто-то является администратором, каждый из ваших пользователей может легко обновить свои привилегии за две или три секунды.Обратите внимание, что эта проблема не всегда очевидна :), но это простой способ объяснить последствия чрезмерного доверия к вводимым пользователем данным.

2 голосов
/ 24 марта 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...