Межсайтовый скриптинг
Содержание:
- Как избежать проблем?
- Предотвращение XSS
- Как защититься от XSS
- Различные типы уязвимости Cross-Site Scripting
- Средства защиты[1]
- Типы XSS-уязвимостей
- Как эксплуатируется уязвимость
- Когда лучше обрабатывать строку
- Сравнение обычного с async, defer и async defer
- XSS: Уязвимость для инъекции
- Справочная информация
Как избежать проблем?
Как вы уже поняли, способа безопасно вставить Javascript в HTML нет. Но есть способы сделать Javascript безопасным для вставки в HTML (почувствуйте разницу). Правда для этого нужно быть предельно внимательным всё время, пока вы пишете что-то внутри тега <script>, особенно если вы вставляете любые данные с помощью шаблонизатора.
Во-первых, вероятность того, что у вас в исходном тексте (даже после минификации) не в строковых литералах встретятся символы крайне мала. Сами вы вряд ли напишете что-то такое, а если злоумышленник что-то сможет написать прямо в теге <script>, то внедрение этих символов будет беспокоить вас в последнюю очередь.
Остается проблема внедрения символов в строки. В этом случае, как и написано в спецификации, всего-то нужно заменить все «» на «», «» на «», а «» на «». Но беда в том, что если вы выводите какую-то структуру с помощью , то вряд ли вы захотите потом её распарсить еще раз, чтобы найти все строковые литералы и заэкранировать в них что-то. Так же не хочется советовать пользоваться другими пакетами для сериализации, где эта проблема уже учтена, потому что ситуации бывают разными, а защититься хочется всегда и решение должно быть универсальным. Поэтому я бы советовал экранировать символы / и ! с помощью обратного слеша уже после сериализации. Эти символы не могут встречаться в JSON нигде кроме как внутри строк, поэтому простая замена будет абсолютно безопасной. Это не изменит последовательность символов «», но она и не представляет опасности, если встречается сама по себе.
Точно так же можно экранировать и отдельные строки.
Другой совет — не встраивайте в тег <script> ничего вообще. Храните данные в местах, где трансформации для вставки данных однозначны и обратимы. Например, в атрибутах других элементов. Правда смотрится это довольно грязно и работает только со строками, JSON придется парсить отдельно.
Но, по-хорошему, конечно, если вы хотите нормально разрабатывать приложения, а не аккуратно ходить по минному полю, нужен надежный способ встраивания скриптов в HTML. Поэтому правильным решением считаю вообще отказаться от тега <script>, как от не безопасного.
Предотвращение XSS
Для предотвращения уязвимостей XSS очень важно применять контекстно-зависимую выходную кодировку. В некоторых случаях этого может быть достаточно для кодирования специальных символов HTML, таких как открывающие и закрывающие теги
В других случаях необходимо правильно применять кодировку URL. Ссылки обычно следует запрещать, если они не начинаются с протокола из белого списка, такого как http:// или https://, что предотвращает использование схем URI, таких как javascript://.
Несмотря на то, что большинство современных веб-браузеров имеют встроенный XSS-фильтр, их не следует рассматривать как альтернативу очистки. Они не могут отлавливать все виды межсайтовых скриптовых атак и не являются слишком умными, что иногда может приводить к ложным срабатываниям, которые могут помешать правильной загрузке некоторых страниц. Фильтр XSS веб-браузера должен быть только «второй линией защиты», и идея состоит в том, чтобы минимизировать влияние существующих уязвимостей.
Разработчики не должны использовать черные списки, поскольку для них существует множество обходных путей. Другой вещью, которую они должны избегать, является удаление опасных функций и символов, поскольку XSS-фильтры браузеров не могут распознавать опасные полезные данные, когда выходные данные подделаны с учетом возможных обходов. При этом единственное рекомендуемое предотвращение XSS — это кодирование (то есть очистка входных данных), как упомянуто выше.
Как защититься от XSS
К нашему огромному счастью, есть простой универсальный инструмент — функция htmlspecialchars() или её иногда применяемый аналог htmlentities().
Как это работает. В HTML есть такая штука как сущности или мнемоники. Это когда я пишу прямо в HTML определённую последовательность символов, например ©, а браузер отображает соответствующий этой мнемонике символ, в данном случае значок копирайта .
Попробуй сам:
Так вот. Когда мы запускаем функцию htmlspecialchars(), она берёт нашу строку и заменяет некоторые символы в ней (кавычки, угловые скобки и т.д.) на мнемоники, чтобы браузер гарантированно вывел нашу строку на экран как строку, не пытаясь выполнять её как код.
Т.е. когда мы введём в нашу форму текст <script>alert(‘hello’)</script>, функция htmlspecialchars() превратит его в <script>alert(‘hello’)</script>. Разумеется, браузер уже не воспримет такой код как javascript и просто выведет на экран как есть.
Проверим:
Теперь какой бы javascript код мы не пытались подставить, он будет просто выводиться в браузер как строка.
Различные типы уязвимости Cross-Site Scripting
Существует в основном три различных типа уязвимости Cross-site Scripting; Stored, Reflected и DOM XSS. Ниже мы рассмотрим более подробно каждого из них.
Stored Cross-site Scripting
Уязвимости Stored Cross-site возникают, когда полезная нагрузка (payload) сохраняется, например, в базе данных, а затем выполняется, когда пользователь открывает страницу в веб-приложении. Stored cross-site scripting очень опасно по ряду причин:
- Полезная нагрузка не видна для фильтра XSS браузера
- Пользователи могут случайно активировать полезную нагрузку, если они посещают уязвимую страницу, в то время как для использования Reflected XSS потребуется специально созданный URL-адрес или особые входные данные.
Пример Stored XSS
Stored XSS-уязвимость может возникнуть, если имя пользователя онлайн-доски объявлений не очищено должным образом при выводе на страницу. В этом случае злоумышленник может вставить вредоносный код при регистрации нового пользователя в форме. Когда имя пользователя отражается на странице доски объявлений, оно будет выглядеть так:
Вышеуказанный вредоносный JavaScript запускается каждый раз, когда пользователь посещает этот раздел форума, и он отправляет злоумышленникам файлы cookie доски объявлений, которые хранятся в браузере пользователя, и затем использует их для кражи сеансов пользователя. Stored XSS может быть очень опасной уязвимостью, поскольку может иметь свойство червя — распространяться, особенно при использовании на популярных страницах.
Например, представьте себе доску объявлений или веб-сайт социальной сети, на котором есть общедоступная страница, которая уязвима для уязвимости stored XSS, такой как страница профиля пользователя. Если злоумышленник может разместить вредоносную полезную нагрузку JavaScript, которая добавляет себя на страницу профиля, вектор атаки выполняется каждый раз, когда посетитель открывает страницу, и полезная нагрузка распространяется с экспоненциальным ростом.
Reflected Cross-site Scripting (XSS)
Reflected XSS-уязвимость возникает, когда пользовательский ввод с URL-адреса или данных POST отражается на странице без сохранения, что позволяет злоумышленнику внедрить вредоносный контент. Это означает, что злоумышленник должен отправить созданный вредоносный URL-адрес или почтовую форму жертве, чтобы вставить полезную нагрузку, и жертва должна щелкнуть ссылку. Этот вид полезной нагрузки также обычно определяется встроенными фильтрами XSS в браузерах пользователя, таких как Chrome, Internet Explorer или Edge.
Пример Reflected XSS
В качестве примера XSS-атак мы будем использовать функцию поиска на новостном веб-сайте, которая работает путем добавления пользовательского ввода, полученного из запроса GET HTTP, к параметру q, как показано в следующем примере:
В результатах поиска веб-сайт отражает содержание запроса, который искал пользователь, например:
Если функция поиска уязвима для уязвимости reflected cross-site scripting, злоумышленник может отправить жертве вредоносный URL-адрес, такой как приведенный ниже:
Когда жертва нажимает на вредоносный URL-адрес, выполняется атака XSS, и на веб-сайте отображается следующее:
Исходный код HTML, который отражает вредоносный код злоумышленника, перенаправляет браузер жертвы на веб-сайт, который контролируется злоумышленником, который затем крадет текущие файлы cookie / токены сеанса пользователя из браузера жертвы для сайта example.com в качестве параметра GET,
Средства защиты[1]
Защита на стороне сервера
- Кодирование управляющих HTML-символов, JavaScript, CSS и URL перед отображением в браузере. Для фильтрации входных параметров можно использовать следующие функции: (для кодирования URL), (для фильтрации HTML).
- Кодирование входных данных. Например с помощью библиотек OWASP Encoding Project, HTML Purifier, htmLawed, Anti-XSS Class.
- Регулярный ручной и автоматизированный анализ безопасности кода и тестирование на проникновение. С использованием таких инструментов, как Nessus, Nikto Web Scanner и OWASP Zed Attack Proxy.
- Указание кодировки на каждой web-странице (например, ISO-8859-1 или UTF-8) до каких-либо пользовательских полей.
- Обеспечение безопасности cookies, которая может быть реализована путём ограничения домена и пути для принимаемых cookies, установки параметра HttpOnly, использованием TLS.
- Использование заголовка Content Security Policy, позволяющего задавать список, в который заносятся желательные источники, с которых можно подгружать различные данные, например, JS, CSS, изображения и пр.
Защита на стороне клиента
- Регулярное обновление браузера до новой версии.
- Установка расширений для браузера, которые будут проверять поля форм, URL, JavaScript и POST-запросы, и, если встречаются скрипты, применять XSS-фильтры для предотвращения их запуска. Примеры подобных расширений: NoScript для FireFox, NotScripts для Chrome и Opera.
Типы XSS-уязвимостей
Не все уязвимости XSS одинаковы, их существует множество типов. Здесь перечислены типы и способы их взаимодействия:
Рисунок 3. Типы XSS-уязвимостей

Уязвимости, вызванные кодом на стороне сервера (Java, PHP, .NET и т. д.):
Традиционные XSS-атаки:
- Отраженные (непостоянные). Отраженная XSS-атака срабатывает, когда пользователь переходит по специально подготовленной ссылке. Эти уязвимости появляются, когда данные, предоставленные веб-клиентом, чаще всего в параметрах HTTP-запроса или в форме HTML, исполняются непосредственно серверными скриптами для синтаксического анализа и отображения страницы результатов для этого клиента, без надлежащей обработки.
- Хранимые (постоянные). Хранимые XSS возможны, когда злоумышленнику удается внедрить на сервер вредоносный код, выполняющийся в браузере каждый раз при обращении к оригинальной странице. Классическим примером этой уязвимости являются форумы, на которых разрешено оставлять комментарии в HTML-формате.
Уязвимости, вызванные кодом на стороне клиента (JavaScript, Visual Basic, Flash и т. д.):
Также известные как DOM-модели:
- Отраженные (непостоянные). То же самое, что и в случае с серверной стороной, только в этом случае атака возможна благодаря тому, что код обрабатывается браузером.
- Хранимые (постоянные). Аналогичны хранимым XSS на стороне сервера, только в этом случае вредоносная составляющая сохраняется на клиентской стороне, используя хранилище браузера.
Уязвимости, вызванные инфраструктурой (браузер, плагины, сервера и т. д.):
Встречаются очень редко, но являются более опасными:
- Инфраструктура на стороне клиента. Происходит, когда вредоносная составляющая производит какие-либо манипуляции с функционалом браузера, например с его XSS-фильтром и т.п.
- Инфраструктура на стороне сервера. Возникает, когда веб-сервер некорректно обрабатывает запросы, позволяя модифицировать их.
- Сеть. Происходит, когда возможно внедриться в связь между клиентом и сервером.
Уязвимости, вызванные пользователем:
- Само-XSS. Часто происходит в результате социальной инженерии, когда пользователь случайно запускает вредоносный код в своем браузере.
Как эксплуатируется уязвимость
Конечно, когда вы просто пишете какой-то код, трудно представить, что вы напишете в строке </script> и не заметите проблем. Как минимум, подсветка синтаксиса даст вам знать, что тег закрылся раньше времени, как максимум, написанный вами код не запустится и вы будете долго искать, что произошло. Но это не является основной проблемой с этой уязвимостью. Проблема возникает там, где вы вставляете какой-то контент в Javascript, когда генерируете HTML. Вот частый кусок кода приложений на реакте с серверным рендерингом:
В </script> может появиться в любом месте, где данные поступают от пользователя или из других систем. не будет менять такие строки при сериализации, потому что они полностью соответствуют формату JSON и Javascript, поэтому они просто попадут на страницу и позволят злоумышленнику выполнить произвольный Javascript в браузере пользователя.
Другой пример:
Тут в строки с соответствующим экранированием записываются пользователя и , который пришел на сервер. И, если в вряд ли будет что-то кроме цифр, то в злоумышленник может запихнуть что угодно.
Но на закрывающем теге </script> приколы не заканчиваются. Опасность представляет и открывающий тег <script>, если перед ним в любом месте есть символы , которые в обычном HTML обозначают начало многострочного комментария. Причем в этом случае вам уже не поможет подсветка синтаксиса большинства редакторов.
Что видит здоровый человек и большинство подсветок синтаксиса в этом коде? Два тега <script>, между которыми находится параграф.
Что видит больной парсер HTML5? Он видит один (!) незакрытый (!) тег <script>, содержащий весь текст со второй строчки до последней.
Я до конца не понимаю, почему это так работает, мне понятно лишь, что встретив где-либо символы , парсер HTML начинает считать открывающие и закрывающие теги <script> и не считает скрипт законченным, пока не будут закрыты все открытые теги <script>. То есть в большинстве случаев этот скрипт будет идти до конца страницы (если только кто-то не смог внедрить еще один лишний закрывающий тег </script> ниже, хе-хе). Если вы до этого не сталкивались с подобным, то можете подумать, что я сейчас шучу. К сожалению, нет. Вот скриншот DOM-дерева примера выше:
Самое неприятное, что в отличие от закрывающего тега </script>, который в Javascript может встретиться только внутри строковых литералов, последовательности символов и могут встретиться и в самом коде! И будут иметь точно такой же эффект.
Когда лучше обрабатывать строку
В интернете часто спорят о том, когда лучше обрабатывать текст, до записи в базу данных или при выводе на экран.
У обработки до записи в базу есть несколько недостатков:
- Мы не можем узнать реальную длину строки, поскольку ООО «Три кота» — это 14 символов, но ООО "Три кота" — уже 24 символа.
- Помимо HTML данные из базы иногда нужно подставлять куда-то ещё, например в word/excel/pdf файлы, где может не быть никаких мнемоник. Придётся декодировать все данные в исходный вид.
В общем, неудобно это. Рекомендую всегда сохранять в базу исходный текст, который ввёл пользователь, а обрабатывать уже при выводе на экран.
Сравнение обычного с async, defer и async defer
Async и defer полностью поддерживаются и, как уже говорилось, интуитивно понятная разница между ними заключается в том, что скрипты с async выполняются сразу. Они не ждут окончания парсинга HTML, полного формирования DOM, а также подгрузки остальных скриптов.
Обычные немодульные <script>
- Приостанавливают парсинг HTML.
- Сразу подгружаются, парсятся и выполняются.
- Гарантируют порядок выполнения относительно других обычных немодульных скриптов.
- Блокируют событие DOMContentLoaded.
- Учитывая всё вышесказанное, такие скрипты не подходят для некритичного кода, поскольку замедляют рендеринг и, как следствие, загрузку динамических веб-приложений.
<script defer>
- Для встроенных (inline) немодульных скриптов defer игнорируется, и код выполняются сразу. Если defer прям сильно нужен, можно воспользоваться обходным путём с участием base64.
- Для встроенных скриптов с указанием type=”module” defer применяется по умолчанию.
- Подгружаются без остановки HTML-парсера.
- Гарантируют порядок выполнения относительно других defer-скриптов (если они внешние — с атрибутом src). Криво работают в IE9.
- Выполняются после окончания парсинга DOM (но перед срабатыванием DOMContentLoaded).
- Блокируют событие DOMContentLoaded (только если скрипт не async defer).
<script async>
- Для встроенных (inline) немодульных скриптов async игнорируется.
- Для встроенных модульных скриптов async .
- Подгружаются без остановки HTML-парсера.
- Выполняются без очереди.
- Не гарантируют порядок выполнения относительно других скриптов с async (также касается модульных скриптов с async).
- Не ждут окончания парсинга HTML. Могут прервать построение DOM (в частности, когда он достаётся из кэша браузера).
- Блокируют событие load (но не DOMContentLoad).
- .
<script async defer>
Воспринимаются как async. В древних брузерных движках, которые не поддерживают async (IE9), работает так же, как defer.
XSS: Уязвимость для инъекции
Любой веб-сайт или приложение имеет несколько мест ввода данных —полей формы до самого URL. Простейший пример вводимых данных — когда мы вписываем имя пользователя и пароль в форму:
Рисунок 1. Форма ввода данных

Наше имя будет храниться в базе данных сайта для последующего взаимодействия с нами. Наверняка, когда вы проходили авторизацию на каком-либо сайте, вы видели персональное приветствие в стиле «Добро пожаловать, Илья». Именно для таких целей имена пользователей хранятся в базе данных.
Инъекцией называется процедура, когда вместо имени или пароля вводится специальная последовательность символов, заставляющая сервер или браузер отреагировать определенным, нужным злоумышленнику образом.
Межсайтовым скриптингом называется инъекция, внедряющая код, который будет выполнять действия в браузере от имени веб-сайта. Это может происходить как с уведомлением пользователя, так и в фоновом режиме, без его ведома.
Рисунок 2. Наглядная схема межсайтового скриптинга

В качестве простейшего примера можно привести элементарный скрипт, показывающий окно с уведомлением. Выглядит он примерно так:
Таблица 1. Скрипт, вызывающий всплывающее окно
<script>alert(‘ЭТО XSS-УЯЗВИМОСТЬ!!!’)</script>
Данный скрипт вызывает окно с надписью «ЭТО XSS-УЯЗВИМОСТЬ!!!». Браузер пользователя воспринимает и выполняет этот скрипт как часть легитимного кода сайта.
Справочная информация
Безопасность в Интернете обеспечивается с помощью многих механизмов, в том числе важной концепцией, известной как правило ограничения домена. Это правило разрешает сценариям, находящимся на страницах одного сайта (недоступная ссылка), доступ к методам и свойствам друг друга без ограничений, но предотвращает доступ к большинству методов и свойств для страниц другого сайта .. Межсайтовый скриптинг использует известные уязвимости в web-приложениях, серверах (или в системных плагинах, относящихся к ним)
Используя одну из них, злоумышленник встраивает вредоносный контент в содержание уже взломанного сайта. В результате пользователь получает объединенный контент в веб-браузере, который был доставлен из надежного источника, и, таким образом, действует в соответствии с разрешениями, предоставленными для этой системы. Сумев внедрить необходимый скрипт в веб-страницу, злоумышленник может получить повышенные привилегии в отношении работы с веб-страницами, cookies и другой информацией, хранящейся в браузере для данного пользователя.
Межсайтовый скриптинг использует известные уязвимости в web-приложениях, серверах (или в системных плагинах, относящихся к ним). Используя одну из них, злоумышленник встраивает вредоносный контент в содержание уже взломанного сайта. В результате пользователь получает объединенный контент в веб-браузере, который был доставлен из надежного источника, и, таким образом, действует в соответствии с разрешениями, предоставленными для этой системы. Сумев внедрить необходимый скрипт в веб-страницу, злоумышленник может получить повышенные привилегии в отношении работы с веб-страницами, cookies и другой информацией, хранящейся в браузере для данного пользователя.
Выражение «межсайтинговый скриптинг» первоначально означало взаимодействие уязвимого веб-приложения с сайтом злоумышленника таким образом, чтобы в контексте атакуемого домена был выполнен JavaScript-код, подготовленный злоумышленником (отражённая или хранимая XSS уязвимость). Постепенно определение стало включать в себя и другие способы внедрения кода, включая использование устойчивых и не относящихся к JavaScript языков (например, ActiveX, Java, VBScript, Flash и даже HTML), создавая путаницу среди новичков в сфере информационной безопасности.
XSS уязвимости зарегистрированы и используются с середины 1990-x годов. Известные сайты, пострадавшие в прошлом, включают такие сайты социальных сетей, как ,
,
MySpace,
YouTube,
и др.



