Защищают ли мой PHP-код от внедрения htmlspecialchars и mysql_real_escape_string? - PullRequest
115 голосов
/ 21 сентября 2008

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

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

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

Ответы [ 6 ]

239 голосов
/ 21 сентября 2008

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

.
10 голосов
/ 21 сентября 2008

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

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

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

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

3 голосов
/ 22 сентября 2008

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

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

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

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

$ result = "ВЫБРАТЬ поля из таблицы Где id = ».Mysql_real_escape_string ($ _ POST [ 'ID']);

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

1 ИЛИ 1 = 1

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

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

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

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

Так что вместо использования

$ result = "SELECT поля из таблицы WHERE id =" .mysqlrealescapestring ("1 OR 1 = 1");

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

$ result = "SELECT поля из таблицы WHERE id =" .Numbers ("1 OR 1 = 1");

и он будет безопасно запускать запрос

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

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

2 голосов
/ 05 октября 2011
$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];

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

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

2 голосов
/ 22 сентября 2008

Важной частью этой головоломки являются контексты. Тот, кто отправляет «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, и использование строкового контекста для целочисленного столбца не вызывает никаких проблем.

0 голосов
/ 17 марта 2017

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

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

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

...