Защищают ли htmlspecialchars и mysql_real_escape_string мой PHP-код от внедрения?

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

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

У меня вопрос: всегда ли этого достаточно? Что нам нужно знать больше? Где эти функции выходят из строя?


person Cheekysoft    schedule 21.09.2008    source источник


Ответы (6)


Когда дело доходит до запросов к базе данных, всегда старайтесь использовать подготовленные параметризованные запросы. Библиотеки 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

person Cheekysoft    schedule 21.09.2008
comment
Единственное, что здесь упущено, это то, что первый пример запроса к БД ... простой intval () решит инъекцию. Всегда используйте intval () вместо mysqlescape ... (), когда требуется число, а не строка. - person Robert K; 09.04.2009
comment
и помните, что использование параметризованных запросов позволит вам всегда обрабатывать данные как данные, а не как код. Используйте библиотеку, такую ​​как PDO, и по возможности используйте параметризованные запросы. - person Cheekysoft; 14.04.2009
comment
@The Wicked Flea Я бы сказал, что приведение типов с (int) лучше. Это быстрее, и вам нужно закрыть на одну скобку с другой стороны меньше. - person alex; 23.10.2009
comment
+1: Мне просто нужно было проголосовать за этот ответ годовалой давности. Это даже подумал меня кое-что о XSS. - person Duroth; 23.10.2009
comment
Два замечания: 1. В первом примере вы будете в безопасности, если вы также заключите параметр в кавычки, например $result = "SELECT fields FROM table WHERE id = '".mysql_real_escape_string($_POST['id'])."'"; 2. Во втором случае (атрибут, содержащий URL-адрес), htmlspecialchars вообще бесполезен; в этих случаях вам следует кодировать ввод, используя схему кодирования URL, например, используя rawurlencode. Таким образом, пользователь не сможет вставить javascript: et al. - person Marcel Korpel; 28.03.2011
comment
@Marcel 1: вроде бы верно, но добавление кавычек было бы неправильным, поскольку тогда у вас была бы база данных, сравнивающая строку с числом; гораздо лучше ограничить переменную только целым числом. 2: Да, конечно! Важно понимать контекст места инъекции и соответствующим образом кодировать - В основном суть этого поста: Подтвердить соответствующим образом и кодировать соответствующим образом; не используйте слепо какую-то конкретную функцию, думая, что она обезопасит вас. - person Cheekysoft; 28.03.2011
comment
«Htmlspecialchars кодирует только двойные кавычки, а не одинарные»: это неверно, это зависит от установленных флагов, см. Его параметры. - person Marcel Korpel; 01.05.2011
comment
Почему вы используете mb_convert_encoding в сочетании с htmlentities? Не уверен, что только htmlentities ()? - person markzzz; 19.12.2011
comment
@markzzz, потому что htmlentities сам по себе уязвим для атак с использованием многобайтовых символов. Хороший справочник по избеганию XSS см. На странице owasp.org/index.php/ - person Cheekysoft; 19.12.2011
comment
Гм, вы можете привести простой пример? Не уверен, что вы имеете в виду под многобайтовыми символами :) - person markzzz; 19.12.2011
comment
@Cheekysoft Мне тоже нужен пример атаки по многобайтовой кодировке - person Templar; 10.06.2014
comment
Это должно быть выделено жирным шрифтом: Take a whitelist approach and only let through the chars which are good. В черном списке всегда что-то упускается. +1 - person Jo Smo; 09.07.2014
comment
@ MartínMolina Нет! не пытайтесь написать универсальную функцию. Всегда используйте механизм, подходящий для того, что вы делаете. При отправке в базу данных используйте связанные параметры; при защите от XSS используйте соответствующую технику кодирования вывода для контекста, в который вы вводите. - person Cheekysoft; 21.07.2014
comment
Даже если вы используете htmlspecialchars ($ string) вне HTML-тегов, вы все равно уязвимы для векторов атаки с использованием многобайтовых кодировок. Не могли бы вы объяснить? этот php кусок небезопасен? echo "<something>" .htmlspecialchars($untrusted_string). "</something>" - person Hello World; 17.12.2014
comment
@HelloWorld Чтобы понять теорию, по которой это было написано, см. stackoverflow.com/questions/1412239 Однако в пяти спустя годы, прошедшие с момента его написания, htmlspecialchars и htmlentities теперь пытаются обнаруживать недопустимые многобайтовые последовательности символов (хотя может быть безопаснее использовать mb_convert_encoding, чем полагаться на собственную ошибку); а начиная с php5.4, системная кодировка по умолчанию - UTF-8; IE6 устарел и не поддерживается. Это должно означать, что в вашем конкретном контексте инъекции вы должны быть в безопасности (но можете получить неожиданное предупреждение, если пользователь отправит искаженный utf) - person Cheekysoft; 06.01.2015
comment
Если мы пропустим htmlspecialchars через всю строку, все эти примеры не будут работать. Поскольку запятые будут преобразованы в их специальные символы. Я бы рекомендовал htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); - person NiCk Newman; 14.08.2015

В дополнение к отличному ответу Cheekysoft:

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

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

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

person MarkR    schedule 21.09.2008

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

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

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

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

$ result = "ВЫБРАТЬ поля ИЗ таблицы WHERE id =" .mysql_real_escape_string ($ _ POST ['id']);

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

1 OR 1=1

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

ВЫБЕРИТЕ поля ИЗ таблицы WHERE id = 1 OR 1 = 1

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

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

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

$ result = "ВЫБРАТЬ поля ИЗ таблицы WHERE id =" .mysqlrealescapestring ("1 OR 1 = 1");

я хотел бы использовать

$ result = "ВЫБРАТЬ поля ИЗ таблицы WHERE id =" .Numbers ("1 OR 1 = 1");

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

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

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

person BrilliantWinter    schedule 22.09.2008
comment
Идеально! Это как раз то, что вам нужно. Первоначальный код не удался, потому что он не подтвердил, что число было числовым. Ваш код делает это. вы должны вызывать Numbers () для всех целочисленных переменных, значения которых происходят из-за пределов кодовой базы. - person Cheekysoft; 23.09.2008
comment
Стоит упомянуть, что intval () отлично подойдет для этого, поскольку PHP автоматически преобразует целые числа в строки за вас. - person Adam Ernst; 13.10.2008
comment
Я предпочитаю intval. Превращает 1abc2 в 1, а не в 12. - person jmucchiello; 06.02.2009
comment
intval лучше, особенно по ID. В большинстве случаев, если он был поврежден, его значение, как указано выше, 1 или 1 = 1. Вы действительно не должны сливать чужие ID. Таким образом, intval вернет правильный идентификатор. После этого вы должны проверить, совпадают ли исходные и очищенные значения. Это отличный способ не только остановить атаки, но и найти злоумышленников. - person triunenature; 17.10.2014
comment
Неправильная строка будет катастрофой, если вы показываете личные данные, вы увидите информацию другого пользователя! вместо этого было бы лучше проверить return preg_match('/^[0-9]+$/',$input) ? $input : 0; - person Frank Forte; 31.01.2016

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

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

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

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

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

person Lucas Oman    schedule 22.09.2008
comment
а затем я начну свой вектор атаки с многобайтового символа 0xbf27, который в вашей базе данных latin1 будет преобразован функцией фильтра как 0xbf5c27 - это один многобайтовый символ, за которым следует одинарная кавычка. - person Cheekysoft; 23.09.2008
comment
Старайтесь не защищаться от единственного известного вектора атаки. В конечном итоге вы будете преследовать свой хвост до конца времен, применяя патч за патчем к вашему коду. Если остановиться и посмотреть на общие случаи, это приведет к более безопасному коду и лучшему мышлению, ориентированному на безопасность. - person Cheekysoft; 23.09.2008
comment
Я согласен; в идеале OP будет использовать подготовленные операторы. - person Lucas Oman; 19.02.2010
comment
Хотя цитирование аргументов, предложенных в этом посте, не является надежным, оно смягчит многие из распространенных атак типа 1 ИЛИ 1 = 1, поэтому его стоит упомянуть. - person Night Owl; 04.03.2013

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

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

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

person cnizzardini    schedule 04.10.2011

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

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

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

person Jarett L    schedule 17.03.2017
comment
почему, о, ПОЧЕМУ, вы бы не включили кавычки вокруг пользовательского ввода в свой оператор sql? - Вопрос ничего не говорит о том, что пользователь не цитирует вводимые данные. - person Quentin; 17.03.2017
comment
Что ж, простое исправление для этого - Ужасное исправление для этого. Это отбрасывает данные. Решение, упомянутое в самом вопросе, является лучшим подходом. - person Quentin; 17.03.2017
comment
хотя я согласен, что вопрос не касается цитирования пользовательского ввода, все же кажется глупым не цитировать ввод. и я бы предпочел выбросить данные, чем вводить неверные данные. как правило, при инъекционной атаке вам все равно НЕ нужны эти данные .... не так ли? - person Jarett L; 17.03.2017
comment
хотя я согласен, что вопрос не касается цитирования пользовательского ввода, все же кажется глупым не цитировать ввод. - Нет, это не так. Вопрос не демонстрирует это так или иначе. - person Quentin; 17.03.2017
comment
Можете ли вы показать пример того, как можно выбросить данные, когда они не хотят, чтобы они были выброшены? Я просто немного запутался и очень хотел бы пояснений .... - person Jarett L; 17.03.2017
comment
как правило, при инъекционной атаке вам все равно НЕ нужны эти данные - следует предполагать, что иногда люди будут предоставлять подлинные данные, которые не являются атакой. Эти данные могут включать ' символов. Форма с запросом имени посетителя может быть использована г-ном. О'Рейли, чтобы взять очень простой пример. - person Quentin; 17.03.2017
comment
Итак, вместо того, чтобы удалить цитату, я не мог бы просто преобразовать ее в элемент html (я не думаю, что element - это то слово, которое я хочу). например,% 27, тогда? (очевидно, я новичок в этом, и хочу избежать обмазывания моего кода проверками SQL-инъекций - person Jarett L; 17.03.2017
comment
@JarettL Либо привыкните использовать заранее подготовленные операторы, либо привыкните к тому, что Bobby Tables уничтожает ваши данные каждый вторник. Параметризованный SQL - это единственный лучший способ защитить себя от SQL-инъекций. Вам не нужно выполнять проверки SQL-инъекций, если вы используете подготовленный оператор. Их чрезвычайно легко реализовать (и, на мой взгляд, они делают код НАМНОГО проще для чтения), они защищают от различных идиосинкразий конкатенации строк и внедрения sql, и, что самое главное, вам не нужно изобретать велосипед, чтобы реализовать его. . - person Siyual; 17.03.2017