Должны ли htmlspecialchars и mysql_real_escape_string сохранить код PHP безопасным от инъекции?

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

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

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

Ответ 1

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

Для более углубленного изучения многобайтовых проблем см. fooobar.com/questions/1621/...

Ответ 2

В дополнение к Cheekysoft отличный ответ:

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

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

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

Ответ 3

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

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

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

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

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

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

1 ИЛИ 1 = 1

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

SELECT полей FROM table WHERE id = 1 ИЛИ 1 = 1

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

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

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

$result = "SELECT fields FROM table WHERE id =".mysqlrealescapestring( "1 OR 1 = 1" );

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

$result = "SELECT fields FROM table WHERE id =".Numbers( "1 OR 1 = 1" );

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

SELECT полей FROM table WHERE id = 111

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

Ответ 4

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

Ответ 5

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

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

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

Ответ 6

почему, о WHY, могли бы вы не включать цитаты вокруг ввода пользователя в вашем SQL-заявлении? кажется довольно глупым! включая цитаты в вашем заявлении sql, сделает "1 или 1 = 1" бесплодной попыткой, нет?

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

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