Подтвердить что ты не робот

Каков правильный способ определить, содержат ли входы строки HTML или нет?

При получении пользовательского ввода в формах я хочу определить, не содержат ли поля "имя пользователя" или "адрес" разметку, которая имеет особое значение в XML (RSS-каналах) или (X) HTML (если отображается).

Итак, какой из них является правильным способом определить, не введен ли введенный ввод каких-либо специальных символов в контексте HTML и XML?

if (mb_strpos($data, '<') === FALSE AND mb_strpos($data, '>') === FALSE)

или

if (htmlspecialchars($data, ENT_NOQUOTES, 'UTF-8') === $data)

или

if (preg_match("/[^\p{L}\-.']/u", $text)) // problem: also caches symbols

Я пропустил что-нибудь еще, например, последовательности байтов или другие сложные способы получить метки разметки вокруг таких вещей, как "javascript:"? Насколько мне известно, все атаки XSS и CSFR требуют < или > вокруг значений, чтобы заставить браузер выполнять код (ну, по крайней мере, из Internet Explorer 6 или новее в любом случае) - это правильно?

Я не ищу что-то, чтобы уменьшить или фильтровать вход. Я просто хочу найти последовательности опасных символов, когда они используются в контексте XML или HTML. (strip_tags() является ужасно опасным. Как говорится в руководстве, он не проверяет неверный HTML.)

Update

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

Обновление 2: Пример

  • Пользователь отправляет ввод
  • if (mb_strpos($data, '<') === FALSE AND mb_strpos($data, '>') === FALSE)
  • Я сохраняю его

Теперь, когда данные находятся в моем приложении, я делаю с ним две вещи: 1) отображение в формате HTML - или 2) отображение внутри элемента формата для редактирования.

Первый безопасен в контексте XML и HTML

<h2><?php print $input; ?></h2>' <xml><item><?php print $input; ?></item></xml>

Вторая форма более опасна, но она все равно должна быть безопасной:

<input value="<?php print htmlspecialchars($input, ENT_QUOTES, 'UTF-8');?>">

Обновление 3: Рабочий код

Вы можете загрузить созданный мной gist и запустить код как текст или HTML-ответ, чтобы посмотреть, о чем я говорю. Эта простая проверка передает http://ha.ckers.org XSS Cheat Sheet, и я не могу найти ничего, что делает это. (Я игнорирую Internet Explorer 6 и ниже).

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

Обновление 4: запрос DOM

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

function not_markup($string)
{
    libxml_use_internal_errors(true);
    if ($xml = simplexml_load_string("<root>$string</root>"))
    {
        return $xml->children()->count() === 0;
    }
}

if (not_markup($_POST['title'])) ...
4b9b3361

Ответ 1

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

<?php
$strings = array();
$strings[] = <<<EOD
    ';alert(String.fromCharCode(88,83,83))//\';alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//\";alert(String.fromCharCode(88,83,83))//--></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>
EOD;
$strings[] = <<<EOD
    '';!--"<XSS>=&{()}
EOD;
$strings[] = <<<EOD
    <SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>
EOD;
$strings[] = <<<EOD
    This is a safe text
EOD;
$strings[] = <<<EOD
    <IMG SRC="javascript:alert('XSS');">
EOD;
$strings[] = <<<EOD
    <IMG SRC=javascript:alert('XSS')>
EOD;
$strings[] = <<<EOD
    <IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>
EOD;
$strings[] = <<<EOD
    perl -e 'print "<IMG SRC=java\0script:alert(\"XSS\")>";' > out
EOD;
$strings[] = <<<EOD
    <SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>
EOD;
$strings[] = <<<EOD
    </TITLE><SCRIPT>alert("XSS");</SCRIPT>
EOD;



libxml_use_internal_errors(true);
$sourceXML = '<root><element>value</element></root>';
$sourceXMLDocument = simplexml_load_string($sourceXML);
$sourceCount = $sourceXMLDocument->children()->count();

foreach( $strings as $string ){
    $unsafe = false;
    $XML = '<root><element>'.$string.'</element></root>';
    $XMLDocument = simplexml_load_string($XML);
    if( $XMLDocument===false ){
        $unsafe = true;
    }else{

        $count = $XMLDocument->children()->count();
        if( $count!=$sourceCount ){
            $unsafe = true;
        }
    }

    echo ($unsafe?'Unsafe':'Safe').': <pre>'.htmlspecialchars($string,ENT_QUOTES,'utf-8').'</pre><br />'."\n";
}
?>

Ответ 2

В вышеприведенном комментарии вы писали:

Просто запретите браузер обрабатывать строку как разметку.

Это совершенно другая проблема с тем, что указано в названии. Подход в названии обычно неправильный. Снятие тегов просто управляет входными данными и может привести к потере данных. Вы когда-нибудь пытались говорить о HTML в блоге, который разбивает теги? Разочарование.

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

Рассмотрим следующие данные:

<strong>Test</strong>

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

Если вы просто выгружаете это в HTML-документ, вы рассматриваете его как HTML. Вы не можете рассматривать это как литеральные данные в этом контексте. Вам нужен HTML, который будет выводить литералы. Вам нужно закодировать его как HTML.

Ваша проблема заключается не в том, что у вас слишком много HTML - это у вас слишком мало. Когда вы выводите <, вы выводите необработанные данные в контексте HTML. Вам нужно преобразовать его в &lt;, который является представлением HTML этих данных перед его выходом.

PHP предлагает несколько различных вариантов для этого. Самое непосредственное - использовать htmlspecialchars(), чтобы преобразовать его в HTML, а затем nl2br() для преобразования линия разбивается на элементы <br>.

Ответ 3

Если вы просто "ищете защиту для print '<h3>' . $name . '</h3>'", то да, по крайней мере, второй подход является адекватным, поскольку он проверяет, будет ли значение интерпретироваться как разметка, если бы оно не было убежали. (В этом случае область, в которой будет отображаться $name, представляет собой содержимое элемента, и только символы &, < и > имеют особое значение, когда они появляются в содержимом элемента.) (Для href и аналогичные атрибуты, может потребоваться проверка на "javascript:", но, как вы сказали в комментарии, это не цель.)

Для официальных источников я могу ссылаться на спецификацию XML:

  • Производство контента в разделе 3.1: здесь контент состоит из элементов, разделов CDATA, инструкций по обработке и комментариев (которые должны начинаться с <), ссылки (которые должны начинаться с &) и символьные данные (которые содержат любой другой юридический символ). (Хотя ведущий > рассматривается как символьные данные в содержимом элемента, многие обычно избегают его вместе с <, и это лучше безопасно, чем жаль, чтобы рассматривать его как особенное.)

  • Создание значения атрибута в разделе 2.3: Действительное значение атрибута состоит из ссылок (которые должны начинаться с &) или (который содержит любой другой юридический символ, но не < или символ кавычки, используемый для обертывания значения атрибута). Если вам нужно разместить строковые входы в атрибутах в дополнение к содержимому элемента, символы " и ' необходимо проверить в дополнение к &, < и, возможно, > (и другим символам, незаконным в XML).

  • Раздел 2.2: Определяет, какие коды кода Юникода легальны в XML. В частности, null является незаконным в документе XML и может отображаться неправильно в HTML.

HTML5 (последний рабочий проект, который находится в процессе разработки, описывает очень продуманный синтаксический анализ алгоритм для HTML-документов:

  • Содержимое элемента соответствует состоянию данных в алгоритме синтаксического анализа. Здесь ввод строки не должен содержать нулевой символ, < (который начинает новый тег) или & (который начинается с символьной ссылки).
  • Значения атрибутов соответствуют "до состояния значения атрибута" в алгоритме синтаксического анализа. Для простоты предположим, что значение атрибута обернуто в двойные кавычки. В этом случае парсер переходит к "значение атрибута (двойное кавычное) состояние" . В этом случае ввод строки не должен содержать нулевой символ, " (который заканчивает значение атрибута) или & (который начинается с символьной ссылки).

Если строковые входы должны быть помещены в значения атрибутов (если их размещение не предназначено исключительно для показа), необходимо учитывать дополнительные соображения. Например, HTML 4 указывает:

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

  • Заменить символьные сущности символами,
  • Игнорировать линейные каналы,
  • Замените каждый возврат каретки или вкладку одним пространством.

Пользовательские агенты могут игнорировать начальное и конечное пробелы в CDATA значения атрибута [.]

Нормализация значения атрибута также указана в XML но, видимо, не в HTML5.

Ответ 4

HTML очиститель делает хорошую работу и очень легко реализовать. Вы также можете использовать фильтр Zend Framework, например Zend_Filter_StripTags.

HTML Очиститель не просто исправляет HTML.

Ответ 5

Думаю, ты ответил на свой вопрос. Функция htmlspecialchars() делает именно то, что вам нужно, но вы не должны использовать ее, пока не напишите ввод пользователя на страницу. Чтобы сохранить его в базе данных, существуют другие функции, такие как mysqli_real_escape_string().

Как правило, можно сказать, что вы должны избегать ввода пользователя только тогда, когда это необходимо, для данной целевой системы:

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

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

Ответ 6

Я, конечно, не эксперт по безопасности, но из того, что я собираю, что-то вроде предложенного вами

if (htmlspecialchars($data, ENT_NOQUOTES, 'UTF-8') === $data)

должен работать, чтобы вы не проходили зараженные строки, поскольку вы получили свое кодирование прямо там.

атаки XSS, которые не требуют '<' или ' > ' полагаются на строку, обрабатываемую в блоке JavaScript прямо там и потом, что, как я прочитал ваш вопрос, не то, что вас беспокоит в этой ситуации.

Ответ 7

Я предлагаю вам взглянуть на функцию xss_clean из CodeIgniter. Я знаю, что вы не хотите чистить, дезинфицировать или фильтровать что угодно. Вы просто хотите "обнаружить плохое поведение" и отвергнуть его. Именно поэтому я рекомендую вам посмотреть на этот код функции.

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

Тогда мой короткий/прямой ответ вам будет следующим:

if (xss_clean($data) === $data)

Теперь вам не нужно использовать всю инфраструктуру CodeIgniter только потому, что вам нужна эта единственная функция, конечно. Но я считаю, что вы можете захватить весь класс CI_Security (at /system/core/Security.php) и сделать несколько изменений, чтобы устранить другие зависимости.

Как вы увидите, код xss_clean довольно сложный, так как XSS-уязвимости действительно есть, и я бы просто доверял ему и не пытаюсь "изобретать это колесо"... ИМХО, вы не можете избавиться от XSS, просто обнаружив дюжину символов.

Ответ 8

Правильный способ определить, содержат ли входы строки HTML-теги, или любая другая разметка, которая имеет особое значение в XML или (X) HTML при отображении (кроме существа), просто

if (mb_strpos($data, '<') === FALSE AND mb_strpos($data, '>') === FALSE)

Вы правы! Все атаки XSS и CSFR требуют < или > вокруг значений, чтобы заставить браузер выполнить код (по крайней мере, от IE6 +).

Учитывая приведенный выходной контекст, этого достаточно для безопасного отображения в формате HTML:

<h2><?php print $input; ?></h2> <xml><item><?php print $input; ?></item></xml>

Конечно, если у нас есть какой-либо объект на входе, например &aacute;, браузер не будет выводить его как &aacute;, а как á, если мы не используем функцию типа htmlspecialchars при выполнении вывода, В этом случае даже < и > будут также безопасными.

В случае использования ввода строки в качестве значения атрибута безопасность зависит от атрибута.

Если атрибут является входным значением, мы должны его процитировать и использовать функцию типа htmlspecialchars, чтобы иметь тот же контент для редактирования.

<input value="<?php print htmlspecialchars($input, ENT_QUOTES, 'UTF-8');?>">

Опять же, даже символы < и > будут здесь безопасными.

Мы можем заключить, что нам не нужно делать какие-либо обнаружения и отклонения ввода, если мы всегда будем использовать htmlspecialchars для вывода его, и наш контекст будет всегда соответствовать вышеуказанным случаям (или в равной степени безопасные).

[И у нас также есть несколько способов безопасно хранить его в базе данных, предотвращая эксплойты SQL.]

Что делать, если пользователь хочет, чтобы его "имя пользователя" было &amp; is not an &? Он не содержит < и >... мы его обнаружим и отклоним? Признаем ли мы это? Как мы его покажем? (Этот ввод дает интересные результаты в новой награде!)

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

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

Ответ 10

Если причина вопроса заключается в предотвращении XSS, есть несколько способов взлома уязвимости XSS. Отличная статья об этом - XSS Cheatsheet на ha.ckers.org.

Но, обнаружение в этом случае бесполезно. Вам нужна только профилактика, и правильное использование htmlspecialchars/htmlentities на ваших текстовых вводах перед сохранением их в вашей базе данных происходит быстрее и лучше, чем обнаружение плохого ввода.

Ответ 11

Вы можете использовать регулярное выражение, если знаете набор символов, которые разрешены. ЕСЛИ символ находится в имени пользователя, которое не разрешено, а затем выдает ошибку:

[a-zA-Z0-9_.-]

Проверьте свои регулярные выражения здесь: http://www.perlfect.com/articles/regextutor.shtml

<?php
$username = "abcdef";
$pattern = '/[a-zA-Z0-9_.-]/';
preg_match($pattern, $username, $matches);
print_r($matches);
?>

Ответ 12

Вы можете использовать функцию strip_tags в PHP. Эта функция будет удалять теги HTML и PHP из данных.

Например, $data​​strong > - это переменная, которая содержит ваш контент, и вы можете использовать это следующим образом:

if (strlen($data) != strlen(strip_tags($data))){
    return false;
} 
else{
    return true;
}

Он проверит разделенный контент на исходный контент. Если оба они равны, то мы можем надеяться, что нет никаких тэгов HTML, и он возвращает true. В противном случае он возвращает false, поскольку обнаружил некоторые HTML-теги.

Ответ 13

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

Нет необходимости устанавливать какую-либо другую инфраструктуру или использовать какое-то долговременное приложение.