Всем привет! В данной статья я хочу рассказать про XSS уязвимости, какие они бывают и откуда их можно ждать. Сразу хочу сказать, что статья предназначена скорее для новичков в теме и не претендует на уникальность или очень глубокое погружение в тему. Так как я работаю в банке и последнее время занимаюсь разработкой фронта системы авторизации и аутентификации пользователей, мне приходится довольно много времени уделять безопасности приложения, потому что последнее чего хочет клиент банка — это компрометации его авторизационных данных:). Поэтому я решил собрать все свои знания и опыт в этой области в кучу и поделиться ими с вами. Ну и вообще тема безопасности сейчас кажется очень актуальной, тк мы чуть ли не каждую неделю слышим истории об утечках данных даже у самых крупных и прогрессивных российских IT компаний. Примечание. Вторая часть статьи: XSS атакует! Не краткий обзор где и как искать уязвимости Пересказывая своими словами определение с Википедии, XSS (Cross‑site scripting) — это тип уязвимости, встречающийся в web приложениях. XSS атаки позволяют внедрить вредоносный скрипт (или как его еще часто называют эксплойт) на страницу приложения, в результате чего у пользователей, посещающих эту страницу, могут украсть данные разной степени чувствительности: куки, сессионные токены, логины с паролями и просто личную информацию о пользователе. Внедрить эксплойт злоумышленники могут различными способами, например оставить комментарий под постом или товаром в онлайн магазине, содержащий скрипт. И, если разработчики web‑приложения не позаботились о валидации данных, то вредоносный скрипт запустится у всех пользователей, открывших комментарии на странице. Такой тип уязвимости называется «сохраняемый», но подробнее об этом чуть позже.  Также, наверно, более популярный способ, когда злоумышленник передает вредоносный пэйлоад прямо в ссылке на наше приложение в параметрах запроса или в хэше, который читается в JS и может быть выполнен. Чаще всего это «отраженные» либо «основанные на DOM» XSS атаки, о них тоже чуть позже. Справедливости ради стоит сказать, что в современных приложениях, написанных на современных фреймворках (где в основном экранируются данные пользовательского ввода) становится всё меньше и меньше возможностей совершить XSS атаку. Также и разработчики браузеров не сидят на месте и улучшают в них безопасность, например с помощью таких вещей как: SOP (Same‑Origin Policy) — политика позволяющая определить, какие стрипты будут иметь доступ к данным, а какие нет, точнее даже с каких ресурсов мы разрешаем скриптам получать данные пользователя. Простыми словами помогает запретить скриптам с одного домена получить доступ к данным пользователя от другого домена. CSP (Content Security Policy) — политика, которая позволяет установить список доверенных источников скриптов, все скрипты из других источников просто будут проигнорированы. Валидация входных данных — современные браузеры также самостоятельно валлидируют входные данные и не позволяют нагло пробрасывать скрипты, экранируя их. Но не смотря на все это XSS всё ещё возможен, да и в целом полезно знать о таких вещах, в конце концов даже на собеседовании могут спросить:) Я хочу рассмотреть 3 основных типа XSS (на самом деле их больше, но эти 3 типа наиболее распространены): Сохраняемый — когда вредоносный скрипт через пользовательский ввод сохраняется в БД приложения и потом тем или иным способом запускается у клиента, посетившего страницу, которая генерится на основе данных из той самой БД. Отраженный — при такой уязвимости база не участвует в доставке вредоносного скрипта жертве. Эксплойт нигде не хранится, атака обычно происходит через URL, когда например вредоносный скрипт пробрасывается через query параметры запроса, «отражается от сервера», где в процессе формирования страницы скрипт добавляется в тело ответа и пользователь получает страницу с уже встроенным в неё скриптом. XSS на основе DOM — особенность данного типа атаки в том, что она эксплуатирует уязвимости DOM (очевидно). В отличии от двух других типов, в этом страница на сервере не меняется. Нам приходит совершенно безопасный HTML, но JS, который уже запустится на клиенте, отработает неправильно из‑за внедренного в него скрипта. Другими словами, главное отличие XSS на основе DOM в том, что эксплойт добавляется на страницу в рантайме, в момент запуска JS и он никогда не покидает пределы браузера. Давайте теперь поподробнее разберем каждый из типов. Как я уже говорил ранее, сохраняемый XSS взаимодействует с жертвой через сервер приложения. Легче всего объяснить принцип работы с помощью простого примера. Представьте, что наше приложение — это маркетплэйс, на котором размещаются различные товары, в каждом товаре есть раздел отзывов, в котором каждый пользователь может оставить собственно отзыв. Если разработчики не побеспокоились о безопасности данного раздела, а реализован он примерно так: В таком случае злоумышленник может добавить свой отзыв: Далее у каждого, кто посмотрит этот комментарий отобразится только «Товар говно», но помимо этого выскочит alert со всеми его куками (вместо этого злоумышленник скорее всего сделает запрос, в теле которого передаст все добытые данные о пользователе к своему серверу). Такая уязвимость направлена на большое количество пользователей, потому что распространяется она, ну скажем, естественным способом, скрипт запустится у всех, кто посетит страницу. В отличии от «отраженного» XSS, для распространения которого часто нужно применять социальную инженерию. Пример, который я описал выше конечно максимально примитивный, и может показаться, что сегодня такую уязвимость словить нереально, но я нашел прошлогоднюю статью, где парень отловил «сохраняемую» XSS уязвимость в Microsoft Teams в 2021 году. Microsoft Teams — Cross Site Scripting (XSS) Bypass CSP | by Numan Turle | Medium В данном случае в проекте было много легаси, доставшегося Microsoft от Skype, но никогда не знаешь, где окажешься завтра. Хотя на самом деле я не знаю, где вы находитесь и сегодня, может у вас на проекте тоже много легаси и стоит проверить реализацию старых решений:) В отраженном XSS реализация доставки вредоносного скрипта выглядит иначе. Скрипт не должен сохраняться на серверах приложения, он попадает жертве через ссылку. Из этого следует очевидный вывод, что каждому отдельному пользователю, на которого осуществляется атака, нужно дать такую ссылку лично, например по почте или во время личной переписки, хотя ссылка может быть размещена на сайте злоумышленника, на который тем или иным способом попала жертва и тыкнула на неё. Но, опять же, скорее всего на этот сайт вы попали по ссылке из email'а или из личной переписки.  Дальше по этой ссылке (в которой в query параметрах зашит скрипт) мы попадаем на страничку, которую сформировал сервер, отталкиваясь от содержания ссылки, и добавляя в страничку все те параметры, что в ней имеются. Не трудно догадаться, что скрипт, который добавил злоумышленник в параметры, тоже попадет в сформированный HTML и благополучно запустится у жертвы. Тут уже злоумышленник может отправить себе ваши куки или собрать другие чувствительные данные со страницы и тоже отправить их себе. Конечно скрипт не из любого query параметра попадет на страницу и запустится, у нас должна быть ещё и «особая» реализация работы с этим параметром в приложении. В качестве примера хочу привести не самую стандартную ситуацию, но зато это случай из жизни, который демонстрирует, что даже сегодня можно запросто проморгать такую уязвимость. В нашем приложении на фронте активно использовались query параметры, на их основе мы понимали, какие именно данные нужно будет отобразить клиенту, какой это тип пользователя, через мобильное ли приложение он открыл нашу страницу или через обычный браузер, ну и т.д. В нашем приложении был SSR и все данные, полученные из query параметров мы просто складывали в стор. Он у нас вместе с другим самым необходимым кодом инлайново добавлялся в HTML и отправлялся клиенту. Таким образом, если пробросить в один из query параметров скрипт, он без проблем оказывался в финальном HTML, формированием которого занимался сервер. По сравнению с сохраняемым XSS, данная уязвимость имеет меньший охват, так как атаке подвергается только тот, кто перешел по ссылке со скриптом. В то время как сохраняемой XSS атаке подвергается любой, кто посетил страницу, на которой разместили эксплойт. Но и обнаружить такую уязвимость сложнее, так как её не получится выявить с помощью статического анализа. Такой тип XSS атак нацелен непосредственно на внедрение скрипта в DOM дерево нашего приложения именно во время отработки JS. Например как и в случае с отраженным XSS, мы можем пробросить вредоносный скрипт через query параметр. Но, в отличии от предыдущего примера, наше приложение не добавит этот скрипт в HTML и вернет пользователю страничку без эксплойта.  После того как клиент загрузил страницу и все необходимые для её работы файлы, у клиента запускается JS, который для тех или иных целей обращается к document.location, чтобы достать оттуда данные из query параметра (в котором лежит наш вредоностный скрипт) и например добавить эти данные на страницу пользователя. После этого скрипт запускается, имея в свою очередь доступ к личным данным пользователя. Учитывая то, что в современной веб индустрии большинство приложений — это SPA, реализованные на React, Angular, Vue и других фреймворках, где большАя часть логики перенесена на сторону клиента и страницы формируются именно на основе работы JS в браузере, XSS уязвимости на основе DOM встречаются чаще всего. Давайте разберем пример: Представьте, что мы работаем над банковским веб-приложением, в котором есть раздел ответов на вопросы, каждый из ответов в нем выглядит как отдельная статья. Банк довольно большой, соответственно вопросов к нему тоже много:) В какой‑то момент принимается решение, что для удобства нужно сделать поиск по разделу: Но также необходима возможность делиться ссылкой на результат поиска. Например, если клиент банка обращается в службу поддержки, чтобы сотрудник мог сразу предоставить ссылку с найденными ответами по интересующей клиента теме. Функцию updateSearchQueryParam мы вызываем каждый раз, когда совершаем поиск, чтобы записать в query параметр то, что ищем. А функцию updateSearchSubtitle  также вызываем при каждом поиске, а также при загрузке страницы, чтобы если в query параметре что‑то было, мы это отобразили. Вот в этот момент в нашу реализацию пробралась уязвимость. Увидев параметр поиска в ссылке и то, что его содержимое попадает на страницу, мы можем попробовать передать скрипт с alert и увидеть уведомление на странице. Вместо alert мы можем сделать что‑то пострашнее и например отправить себе куки пользователя. Кстати говоря, такую уязвимость всё ещё можно отследить на стороне сервера. Если мы пишем логи всех запросов, в них будет видно, что приходил подозрительный запрос со скриптом в значении одного из query параметров. Но, как я говорил ранее, бывают случаи, когда скрипт не покидает границ браузера. Например, если в нашем приложении мы работаем не с query параметром, а с hash. Как известно, то что мы пишем в hash ссылке не улетает на сервер, но JS без проблем может работать с тем, что мы туда передали. Скажу ещё пару слов о других типах XSS атак, они не так распространены, но думаю знать об их существовании будет не лишним. mXSS или XSS с мутациями — довольно свежий вид XSS атаки, о котором впервые упомянули в 2013. Для реализации такой атаки нужны глубокие познания в том, как работают браузеры и какие механизмы они используют для борьбы с XSS. Этот вид атаки эксплуатирует механизм очистки и санитайзинга пользовательского ввода браузером. Таким образом с виду нерабочий скрипт, после прохождения очистки браузером становится вполне валидным и может причинить ущерб клиенту, да и компании, в целом. Blind XSS (Слепая XSS) — это подмножество сохраняемого XSS, только запуститься эксплойт может далеко не сразу и даже не обязательно в том же приложении. Например, через форму обратной связи, злоумышленник отправляет отзыв или вопрос, в который встраивает скрипт. Далее сотрудник службы поддержки открывает данное сообщение, после чего и запускается скрипт. Как вы понимаете, это запросто может быть другое приложение, какой‑нибудь сервис для администрирования нашего сайта. Self XSS — вид атаки, при котором жертва самостоятельно запускает вредоносный скрипт на своем устройстве, в основном, в консоли браузера. Выглядит это примерно так: злоумышленник делает рассылку в соцсетях, по почте или даже в личном сообщении, где, в целях безопасности, или чтобы помочь взломать сайт (мол сделай это и получишь доступ к учетке своей бывшей), предлагает открыть консоль и ввести код, который скорее всего отправит ваши персональные данные мошенникам, вот собственно и всё. Такую уязвимость по правде говоря сложно вообще назвать уязвимостью, так как это просто социальная инженерия. На этом краткий обзор окончен, в другой статье погружусь в тему уже поглубже и расскажу, как искать XSS уязвимости и самое главное, как с ними бороться. UPD: Благодарю mrkaban за здравое замечание по поводу нэйминга и по поводу забытой мной Self XSS, все добавил, все поправил. Подборка статей из блога: XSS атакует! Не краткий обзор где и как искать уязвимости Как настраивать аналитику, если у вас Backend Driven UI Разбираюсь в мок-серверах и пишу свой RTK query: что мы от него хотим и зачем Как мы ведём требования к ПО: формализация Как мы переходили на React-router v6: подводные камни и альтернативы Улучшаем качество кода React-приложения с помощью Compound Components Зачем нам Reactive и как его готовить Багатон как в первый раз Почему мы ошибаемся при первоначальной оценке фич? Немного попробовали Chaos Engineering и вот что поняли Что такое Shared UI, как он нам помог и причём тут микросервисы Как перестать волноваться и полюбить хакатоны Также подписывайтесь на Телеграм-канал Alfa Digital — там мы постим новости, опросы, видео с митапов, краткие выжимки из статей, иногда шутим.  Фронтендер