Вредоносный JavaScript
Мое мнение, состоящее в том, что от внедряемых вредоносных браузерных скриптов (хранимые
XSS-атаки) проще и эффективнее защищаться средствами браузеров, было изложено ранее:
.
Браузерная защита от JavaScript-а, состоящая в добавлении к html-страницам сайта
фильтрующего кода, надо полагать, является надежной, тем не менее, наличие такой защиты
не отменяет необходимости использовать еще и серверный фильтр. В отношении тех же XSS-атак
на сервере можно организовать дополнительную линию обороны. Надо помнить и о возможности
внедрения злоумышленником в html-сообщение, отправляемое с сайта, не браузерных, а серверных
скриптов (php), в распознавании которых браузер будет не силен.
Атакующий скрипт, хоть браузерный, хоть серверный — это ведь программа, надо думать,
что программа всегда будет иметь некоторые символьные отличия от «чистого» html. Попробуем
найти такие отличия и использовать их для построения html-фильтра на сервере. Ниже — примеры
вредоносного JavaScript.
XSS:
Какой-то текст
Какой-то текст
Зашифрованный XSS:
Какой-то текст
Браузеры восстанавливают текст из символьных примитивов не только находящийся внутри
html-контейнеров (между открывающим и закрывающим тегом), но и внутри самих тегов (между
< и >). В http-адресах допускается url-кодирование. Указанное осложняет распознавание
вредоносного кода на стороне сервера, так как одна и та же символьная последовательность
может быть представлена разными способами.
XSS-черви:
Вышеприведенные XSS-черви — всего лишь несколько из множества присланных на проводимый
в январе 2008 года Робертом Хансеном (aka RSnake) конкурс на минимального (самого короткого)
вредоносного JavaScript-червя (итоги конкурса
).
Признаки XSS-атак
XSS-скрипт — это программа, обращающаяся к объектам DOM (Document Object Model) и их методам.
Иное едва ли будет сколько-нибудь вредоносным. Например, JavaScript-строка
onckick=»var a = «xss»»
не затрагивает объектную модель документа, поэтому, даже будучи внедренной в html-тег,
такая строка безвредна. Только манипулируя объектами html-документа и их методами,
хакер способен причинить заметный вред сайту. Например, строка
onmousemove=»document.getElementsByTagName(«body»).innerHTML=»XSS»»
уже заменяет содержимое страницы.
Признак обращения к методам DOM — это круглые скобки, также точки, стоящие слева от знака
равенства. Круглые скобки могут использоваться и в html — для задания цвета в формате
rgb()
, однако, цвет шрифта и фона в html задается, как минимум, еще тремя способами.
Поэтому круглыми скобками можно пожертвовать без ущерба для выразительности html-текста.
Необходимо принять правилом, что скобки внутри тега (то есть, между < и >) — это
очень опасно, если мы получили на сервер сообщение от пользователя, в этом сообщении
обнаруживаются скобки внутри тегов, то самое уместное, что мы должны сделать — это
заблокировать такое сообщение.
Точка может содержаться в html-тегах: при задании адреса ссылки (тег
);
при задании размера 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(«//», «», $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(«/<[^>]+?\.+?\=[^>]+?>/i», «», $message);
// вырезаем каждый тег, в котором есть последовательность «% a-z» или «% 0-9»
$message = preg_replace(«/<[^>]+?\%+?[^>]+?>/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(«$», «$», $message);
$message = str_replace(«_», «_», $message);
$message = str_replace(«.», «.», $message);
$message = str_replace(chr(92), «\», $message); // \
$message = str_replace(«(«, «(«, $message);
$message = str_replace(«)», «)», $message);
$message = str_replace(«[«, «[«, $message);
$message = str_replace(«]», «]», $message);
$message = str_replace(«{«, «{«, $message);
$message = str_replace(«}», «}», $message);
$message = str_replace(«?», «?», $message);
// теперь сообщение проверено, скрипты в нем обезврежены
Следует заметить, что фильтр не удаляет парные теги. Допустим, мы получили
Фильтр вырежет только
, но парный (закрывающий) тег