Ранее сегодня был задан вопрос относительно стратегий проверки ввода в веб-приложениях .
Главный ответ, на момент написания, предлагает в PHP
просто использовать htmlspecialchars
и mysql_real_escape_string
.
Мой вопрос: это всегда достаточно? Больше мы должны знать? Где эти функции ломаются?
Когда дело доходит до запросов к базе данных, всегда старайтесь использовать подготовленные параметризованные запросы. Это поддерживает библиотеки mysqli
и PDO
. Это бесконечно безопаснее, чем использование escaping-функций, таких как 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-тега, поэтому нам не нужно <or> делать что-либо опасное. Наш вектор атаки может быть просто 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.
В дополнение к отличному ответу Cheekysoft:
На самом деле не существует серебряной пули для предотвращения встраивания HTML (например, межсайтовый скриптинг), но вы можете достичь этого легче, если используете библиотеку или систему шаблонов для вывода HTML. Прочитайте документацию для этого, чтобы как можно безопасно избегать действий.
В HTML, вещи должны быть экранированы по-разному в зависимости от контекста. Это особенно верно для строк, помещенных в Javascript.
Я определенно согласился бы с вышеупомянутыми сообщениями, но у меня есть одна небольшая вещь, чтобы добавить ответ на ответ Cheekysoft, а именно:
Когда дело доходит до запросов к базе данных, всегда старайтесь использовать подготовленные параметризованные запросы. Это поддерживает библиотеки mysqli и PDO. Это бесконечно безопаснее, чем использование escaping-функций, таких как mysql_real_escape_string.
Да, mysql_real_escape_string фактически является просто функцией экранирования строки. Это не волшебная пуля. Все, что он сделает, это избежать опасных символов, чтобы их можно было безопасно использовать в одной строке запроса. Однако, если вы не будете заранее дезинфицировать свои входы, тогда вы будете уязвимы для определенных векторов атаки.
Представьте себе следующий SQL:
$ result = "SELECT fields FROM table WHERE id =" .mysql_real_escape_string ($ _ POST ['id']);
Вы должны уметь видеть, что это уязвимо для использования. Представьте, что параметр id содержит общий вектор атаки:
1 ИЛИ 1 = 1
Там нет рискованных символов для кодирования, поэтому он пройдет прямо через экранирующий фильтр. Оставляя нас:
SELECT полей FROM table WHERE id = 1 OR 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 на ваш сайт;)
Важной частью этой головоломки является контекст. Кто-то отправляет «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 и использование контекста строки для целочисленного столбца не вызывает никаких проблем.
$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];
Хорошо работает, даже лучше на 64-битных системах. Остерегайтесь системных ограничений при обращении к большим номерам, но для ИД базы данных это работает отлично в 99% случаев.
Вы должны использовать одну функцию / метод для очистки ваших значений. Даже если эта функция является только оболочкой для mysql_real_escape_string (). Зачем? Поскольку в один прекрасный день, когда вы используете эксплойт для вашего предпочтительного метода очистки данных, вам нужно только обновить его на одном месте, а не общесистемную находку и замену.
почему, о, ПОЧЕМУ, вы не включали бы кавычки вокруг ввода пользователя в свой SQL-запрос? кажется довольно глупым! включая цитаты в вашем заявлении sql, сделает «1 или 1 = 1» бесплодной попыткой, нет?
так что теперь вы скажете: «Что делать, если пользователь вводит котировку (или двойные кавычки)»?
хорошо, легко исправить это: просто удалите кавычки ввода пользователя. например: input =~ s/'//g;
, теперь мне все равно кажется, что пользовательский вход будет защищен …