Защищают ли htmlspecialchars и реальная escape-строка mysql мой PHP-код от инъекций?


Ранее сегодня был задан вопрос относительно стратегий проверки ввода в веб-приложениях.

Верхний ответ на момент написания предлагает в PHP просто использовать htmlspecialchars и mysql_real_escape_string.

Мой вопрос: всегда ли этого достаточно? Есть ли еще что-то, что мы должны знать? Где эти функции нарушаются?

Author: Community, 2008-09-21

6 answers

Когда дело доходит до запросов к базе данных, всегда старайтесь использовать подготовленные параметризованные запросы. Библиотеки mysqli и PDO поддерживают это. Это бесконечно безопаснее, чем использование экранирующих функций, таких как mysql_real_escape_string.

Да, mysql_real_escape_string фактически является просто функцией экранирования строки. Это не волшебная пуля. Все, что он будет делать, - это избегать опасных символов, чтобы их можно было безопасно использовать в одной строке запроса. Однако, если вы не очистите свои входные данные заранее, то вы будете уязвим для определенных векторов атаки.

Представьте себе следующий SQL:

$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']);

Вы должны быть в состоянии видеть, что это уязвимо для использования.
Представьте, что параметр id содержит общий вектор атаки:

1 OR 1=1

Там нет рискованных символов для кодирования, поэтому он пройдет прямо через экранирующий фильтр. Покидая нас:

SELECT fields FROM table WHERE id= 1 OR 1=1

, который является прекрасным вектором SQL-инъекции и позволит злоумышленнику вернуть все строки. Или

1 or is_admin=1 order by id limit 1

, который производит

SELECT fields FROM table WHERE id=1 or is_admin=1 order by id limit 1

, что позволяет злоумышленнику вернуть данные первого администратора в этом полностью вымышленном примере.

Хотя эти функции полезны, их следует использовать с осторожностью. Вам необходимо убедиться, что все веб-входы в той или иной степени проверены. В этом случае мы видим, что нас можно использовать, потому что мы не проверили, что переменная, которую мы использовали в качестве числа, на самом деле была числовой. В PHP вы должны широко использовать набор функции для проверки того, что входные данные являются целыми числами, числами с плавающей запятой, буквенно-цифровыми и т.д. Но когда дело доходит до SQL, больше всего обращайте внимание на значение подготовленного оператора. Приведенный выше код был бы безопасным, если бы это был подготовленный оператор, поскольку функции базы данных знали бы, что 1 OR 1=1 не является допустимым литералом.

Что касается htmlspecialchars(). Это само по себе минное поле.

Реальная проблема в PHP заключается в том, что в нем есть целый набор различных функций экранирования, связанных с html, и нет четких указаний от того, какие именно функции что делают.

Во-первых, если вы находитесь внутри HTML-тега, у вас серьезные проблемы. Посмотрите на

echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';

Мы уже находимся внутри HTML-тега, поэтому нам не нужно делать что-либо опасное. Наш вектор атаки может быть просто javascript:alert(document.cookie)

Теперь результирующий HTML выглядит как

<img src= "javascript:alert(document.cookie)" />

Атака проходит прямо насквозь.

Становится все хуже. Почему? потому что htmlspecialchars (при таком вызове) кодирует только двойные кавычки, а не одинарные. Так что, если бы у нас было

echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />";

Наш злой злоумышленник теперь может вводить совершенно новые параметры

pic.png' onclick='location.href=xxx' onmouseover='...

Дает нам

<img src='pic.png' onclick='location.href=xxx' onmouseover='...' />

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

Даже если вы используете htmlspecialchars($string) вне HTML-тегов, вы все еще уязвим для векторов атаки с многобайтовой кодировкой.

Наиболее эффективным из вас может быть использование комбинации mb_convert_encoding и htmlentities следующим образом.

$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');
$str = htmlentities($str, ENT_QUOTES, 'UTF-8');

Даже это делает IE6 уязвимым из-за того, как он обрабатывает UTF. Однако вы можете вернуться к более ограниченной кодировке, такой как ISO-8859-1, до тех пор, пока использование IE6 не прекратится.

Для более глубокого изучения многобайтовых проблем см. https://stackoverflow.com/a/12118602/1820

 231
Author: Cheekysoft, 2017-05-23 11:47:32

В дополнение к превосходному ответу Cheekysoft:

  • Да, они обеспечат вам безопасность, но только в том случае, если они используются абсолютно правильно. Используйте их неправильно, и вы все равно будете уязвимы, и у вас могут возникнуть другие проблемы (например, повреждение данных)
  • Пожалуйста, используйте вместо этого параметризованные запросы (как указано выше). Вы можете использовать их, например, через PDO или через оболочку, такую как PEAR DB
  • Убедитесь, что magic_quotes_gpc и magic_quotes_runtime всегда отключены, и никогда случайно включиться, даже ненадолго. Это ранняя и глубоко ошибочная попытка разработчиков PHP предотвратить проблемы безопасности (которые уничтожают данные)

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

В HTML все должно быть быть экранированным по-разному в зависимости от контекста. Это особенно верно для строк, помещаемых в Javascript.

 9
Author: MarkR, 2008-09-21 09:10:00

Я бы определенно согласился с приведенными выше сообщениями, но у меня есть одна маленькая вещь, которую я должен добавить в ответ на ответ Cheekysoft, а именно:

Когда дело доходит до запросов к базе данных, всегда старайтесь использовать подготовленные параметризованные запросы. mysqli и Библиотеки PDO поддерживают это. Это бесконечно безопаснее, чем использование экранирующих функций, таких как mysql_real_escape_string.

Да, mysql_real_escape_string фактически является просто функцией экранирования строки. Это не является волшебная пуля. Все, что он будет делать, - это избегать опасных символов, чтобы их можно было безопасно использовать в одной строке запроса. Однако, если вы заранее не очистите свои входные данные, вы будете уязвимы для определенных векторов атаки.

Представьте себе следующий SQL:

$результат= "ВЫБЕРИТЕ поля ИЗ таблицы ГДЕ id= ".строка mysql_real_escape_string($_POST['идентификатор']);

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

1 ИЛИ 1=1

Там нет рискованных символов для кодирования, поэтому он пройдет прямо через выходящий фильтр. Уходя мы:

ВЫБЕРИТЕ поля ИЗ таблицы, ГДЕ id = 1 ИЛИ 1=1

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

function Numbers($input) {
  $input = preg_replace("/[^0-9]/","", $input);
  if($input == '') $input = 0;
  return $input;
}

Поэтому вместо использования

$результат = "ВЫБЕРИТЕ поля ИЗ таблицы, ГДЕ id =".mysqlrealescapestring("1 ИЛИ 1=1");

Я бы использовал

$результат= "ВЫБЕРИТЕ поля ИЗ таблицы, ГДЕ id =".Числа("1 ИЛИ 1=1");

И он безопасно выполнит запрос

ВЫБЕРИТЕ поля ИЗ таблицы, ГДЕ id = 111

Конечно, это просто помешало ему отобразить правильную строку, но я не думаю, что это большая проблема для тот, кто пытается внедрить sql на ваш сайт;)

 3
Author: BrilliantWinter, 2008-09-22 17:26:11

Важной частью этой головоломки являются контексты. Кто-то, отправляющий "1 ИЛИ 1=1" в качестве идентификатора, не является проблемой, если вы цитируете каждый аргумент в своем запросе:

SELECT fields FROM table WHERE id='".mysql_real_escape_string($_GET['id'])."'"

Что приводит к:

SELECT fields FROM table WHERE id='1 OR 1=1'

Что неэффективно. Поскольку вы экранируете строку, входные данные не могут вырваться из контекста строки. Я протестировал это до версии 5.0.45 MySQL, и использование контекста строки для целочисленного столбца не вызывает никаких проблем.

 2
Author: Lucas Oman, 2008-09-22 17:38:54
$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];

Работает хорошо, даже лучше на 64-битных системах. Однако остерегайтесь системных ограничений на адресацию больших чисел, но для идентификаторов баз данных это отлично работает в 99 % случаев.

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

 2
Author: cnizzardini, 2011-10-04 21:32:26

Почему, о, ПОЧЕМУ бы вам не включить кавычки вокруг пользовательского ввода в свой оператор sql? кажется довольно глупым не делать этого! включение кавычек в вашу инструкцию sql сделало бы "1 или 1=1" бесплодной попыткой, не так ли?

Итак, теперь вы скажете: "Что, если пользователь включит кавычку (или двойные кавычки) во входные данные?"

Ну, это легко исправить: просто удалите введенные пользователем кавычки. например: input =~ s/'//g;. теперь, мне все равно кажется, что пользовательский ввод будет защищен...

 -3
Author: Jarett L, 2017-03-17 19:02:46