Знакомимся с уязвимостью XSS
Last updated
Last updated
В 2009 году в блоге Microsoft MSDN была опубликована статья под названием указывая на то, что впервые XSS появилась примерно в 1999 году, то есть в предыдущем столетии.
Хотя статья 2009 года заканчивалась надеждой на "смерть" XSS, а не его рождение:
Давайте надеяться, что через десять лет мы будем праздновать смерть, а не рождение Cross-Site Scripting!
Как мы знаем, даже спустя 20 лет XSS остается популярной уязвимостью. От малоизвестных сайтов небольших компаний до гигантов вроде Facebook или Google – уязвимости XSS появляются до сих пор. Это говорит о том, что защита от подобных атак – непростая задача.
Теперь давайте разберемся, что такое XSS.
XSS расшифровывается как Cross-site scripting, но не CSS, потому что это уже занято каскадными таблицами стилей (Cascading Style Sheets).
С современной точки зрения название XSS не совсем верно, поскольку многие атаки XSS не ограничиваются "cross-site" (между сайтами). Разницу между "cross" и "site" я объясню позже. Это крайне важно для понимания безопасности фронтенда.
Проще говоря, XSS позволяет злоумышленникам запускать JavaScript-код на сайтах других людей.
Например, представьте себе сайт, написанный следующим образом:
Если я зайду на /index.php?name=xss, на странице появится "Hello, xss", что может показаться нормальным.
Но что, если я посещу /index.php?name=<script>alert(1)</script>? Результат будет следующим:
Содержимое внутри <script> будет интерпретировано как код JavaScript и он будет выполнен. На экране появится всплывающее окно с сообщением, и благодаря этому я смогу запускать код JavaScript на чужом сайте.
Хотя большинство примеров XSS демонстрируют выполнение alert(1) для подтверждения возможности запуска кода, не думайте, что XSS ограничен этим. Это используется только для демонстрации наличия уязвимости.
Уязвимость XSS означает, что код может быть выполнен на чужом сайте, позволяя выполнять различные действия. Например, можно украсть все, что хранится в localStorage, включая токены аутентификации. С украденным токеном можно войти на сайт под чужим именем.
Поэтому некоторые выступают за хранение токенов аутентификации в cookies, а не в localStorage. localStorage можно украсть, но если у cookie установлен флаг HttpOnly, к нему нельзя получить доступ с помощью JavaScript, следовательно, его нельзя украсть.
Если сайт не использует флаг HttpOnly, можно использовать document.cookie или обновленный API cookieStore для получения файлов cookie сайта. Даже если украсть не получится, можно напрямую использовать fetch() для вызова API и увидеть, какими функциями можно манипулировать на сайте.
Например, предположим, у YouTube есть уязвимость XSS. Злоумышленники могут использовать ее для добавления или удаления видео, кражи истории просмотров и других данных, а также для выполнения практически любых действий, которые может выполнить обычный пользователь.
Вы когда-нибудь задумывались, почему многие сайты требуют повторного ввода текущего пароля при его смене? Разве мы уже не вошли в систему? Зачем нам его снова вводить? Неужели я не знаю свой собственный пароль при смене?
Вы, конечно, знаете свой пароль, но злоумышленники - нет.
В случае функции смены пароля серверная часть может предоставлять API под названием /updatePassword, который требует параметров currentPassword и newPassword. После проверки подлинности пароль можно изменить.
Даже если злоумышленник найдет и использует уязвимость XSS, он не сможет изменить ваш пароль, потому что не знает его текущего значения.
С другой стороны, если при смене пароля не требуется currentPassword, злоумышленник может напрямую изменить ваш пароль с помощью XSS и завладеть вашим аккаунтом. Токен авторизации, полученный через XSS, имеет срок действия и истекает, но если злоумышленник напрямую изменит ваш пароль, он сможет использовать ваши учетные данные для последующего входа в аккаунт.
Поэтому для многих важных операций требуется повторный ввод пароля или даже наличие второго пароля, одной из целей которого является защита от подобных ситуаций.
Уязвимости XSS возникают из-за того, что пользовательский ввод напрямую попадает на страницу, без предварительной обработки, позволяя пользователям вводить вредоносные данные и внедрять JavaScript-код.
Возможно, вы слышали о различных классификациях XSS, таких как Reflected, Stored и DOM-based, но эти методы классификации существуют уже более двадцати лет и могут не подходить для современного контекста. Поэтому я считаю, что XSS можно рассматривать с двух точек зрения:
Например, в упомянутом ранее примере на PHP содержимое злоумышленника напрямую попадает на сервер, поэтому когда браузер получает HTML, он уже содержит вредоносный код XSS.
Вот еще один пример. Ниже приведен файл HTML:
Аналогично, мы можем вставить любой желаемый контент с помощью /index.html?name=<script>alert(1)</script>, но на этот раз контент попадает на страницу через innerHTML.
В чем разница?
Разница в том, что в приведенном выше примере оповещение не сработает, потому что при использовании innerHTML внедренный <script> не выполняется. Таким образом, злоумышленник должен адаптировать вредоносный код XSS для выполнения атаки.
В упомянутых ранее примерах контент непосредственно отображается из строки запроса на странице, поэтому вредоносный код атаки нигде не хранится.
Таким образом, чтобы провести атаку, нам нужно найти способ заставить жертву перейти по ссылке с вредоносным кодом XSS, чтобы провести атаку. Конечно, для снижения этого импакта могут использоваться другие методы или их комбинации, например, использование сокращенных URL-адресов для скрытия любых аномалий.
В этой ситуации целью вашей атаки является, по сути, один человек.
Есть и другая ситуация, которая относительно проста, например, доска комментариев. Предположим, что в комментарии можно вставлять HTML-код без фильтрации. Мы можем оставить контент с тегами <script>. В результате будет атакован любой, кто просматривает эту доску комментариев, и вашей целью становится вся аудитория пользователей, что расширяет масштаб воздействия.
Только представьте, если бы в публикациях Facebook была уязвимость XSS, то каждый, кто увидел публикацию, был бы атакован. Это могло бы даже стать червем, то есть самовоспроизводиться, как червь, используя XSS для помощи жертвам в публикациях, в результате чего было бы атаковано еще больше людей.
Самый известный реальный случай - это MySpace, популярная социальная сеть в 2005 году. 19-летний парень по имени Samy Kamkar обнаружил уязвимость XSS на странице профиля. Он использовал уязвимость, чтобы заставить жертв добавлять его в друзья, а затем внедрял вредоносный код XSS в их профили. В результате в течение 18 часов было заражено более 1 миллиона пользователей, что вынудило MySpace временно закрыть сайт для удаления этих зараженных профилей.
Этот случай демонстрирует эффект атаки XSS.
Помимо классификации XSS по "источнику вредоносного кода", есть и другие способы. Ниже я представлю еще две дополнительные классификации XSS, хотя они менее распространены, но их все равно полезно знать.
Self-XSS - "атака на себя". Например, открытие инструментов разработчика веб-страницы и самостоятельная вставка кода JavaScript является формой self-XSS. Некоторые сайты, как Facebook, специально предупреждают об этом:
Ранее мы обсуждали XSS, направленную на атаку других, так как они могут видеть ваш вредоносный код. Однако иногда его можете видеть только вы.
Например, предположим, что в поле номера телефона есть уязвимость XSS, но проблема в том, что номер телефона является личной информацией, поэтому его можете видеть только вы на своей странице настроек. Другие его не видят. Такая контекст называется self-XSS, когда при открытии страницы настроек вы видите только всплывающее окно alert().
Хоть это и может показаться бесполезным, но в сочетании с другими уязвимостями импакт от такой уязвимости может заметно повыситься.
Слепая XSS означает "XSS, выполняемый в месте и во время, которые вы не можете видеть".
Рассмотрим еще один пример. Представьте, что есть платформа электронной коммерции, и после тестирования вы не обнаружили никаких проблем в полях и не нашли уязвимостей XSS. Однако у платформы есть внутренний портал, где можно просматривать все данные о заказах, и в этом портале есть уязвимость. Разработчики забыли добавить валидацию поля имени, поэтому через него можно выполнить XSS.
В статье OWASP 2008 года под названием упоминаются несколько случаев червей XSS.
В таком случае мы обычно не узнаем о наличии уязвимости этом во время тестирования, потому что у нас нет доступа к внутренней системе, и мы можем даже не знать о ее существовании. Чтобы проверить такую ситуацию, вам нужно изменить содержимое вредоносного кода XSS с alert() на код, который отправляет пакет, например fetch(''). Таким образом, когда XSS срабатывает в невидимом нам месте, результат его работы можно будет заметить на нашем сервера.
Существуют готовые сервисы, такие как которые предоставляют платформу для удобного наблюдения за тем, срабатывает ли XSS. Если она срабатывает, на экране отобразится URL-адрес, на котором он сработал, и другая информация.
Говоря о реальных случаях, rioncool22 сообщил об уязвимости в Shopify в 2020 году: . Они добавили сотрудника в портале продавцов Shopify и вставили код XSS в поле имени. Хоть JavaScript не сработал в портале продавцов, он сработал во внутреннем портале Shopify, и они получили награду в размере 3000 долларов.