Client-Side Fundamental
  • Добро пожаловать
  • Глава 1 - Начало работы с XSS
    • Браузерная модель безопасности
    • Знакомимся с уязвимостью XSS
    • Более глубокое понимание XSS
    • Опасный псевдопротокол javascript
  • Глава 2 - Защита и Обход для XSS
    • Первая линия обороны от XSS - Sanitization
    • Вторая линия обороны от XSS - CSP (Content Security Policy)
    • Третья линия обороны против XSS - сокращение области воздействия
    • Последние методы защиты от XSS - Trusted Types и встроенный Sanitizer API
    • Обход защитных мер - Обычные способы обхода CSP
    • Обход защитных мер - Mutation XSS
    • Самая опасная XSS - Universal XSS
  • Глава 3 - Атаки без JavaScript
    • Кто сказал, что для атаки обязательно выполнять JavaScript?
    • Prototype Pollution - Эксплуатация цепочки прототипов
    • Может ли HTML влиять на JavaScript - Введение в DOM clobbering
    • Template Injection in Frontend - CSTI
    • CSS Injection - Атака с использованием только CSS (Часть 1)
    • CSS Injection - Атака с использованием только CSS (Часть 2)
    • Можно ли атаковать, используя только HTML
  • Глава 4 - Межсайтовые атаки
    • Same-origin Policy и Same-Site
    • Введение в Cross-Origin Resource Sharing (CORS)
    • Проблемы Cross-Origin безопасности
    • Cross-Site Request Forgery (CSRF)
    • Спаситель от CSRF - Same-site cookie
    • От same-site до главного site
    • Интересная и практичная Cookie Bomb
  • Глава 5 - Другие интересные темы
    • То, что вы видите, это не то, что вы получаете - Clickjacking
    • Эксплуатация MIME Sniffing
    • Атаки на цепочку поставок во фронтенде - Attacking Downstream from Upstream
    • Атаки на веб-фронтенд в Web3
    • Самая интересная атака на побочные каналы фронтенда - XSLeaks (Часть 1)
    • Самая интересная атака на побочные каналы фронтенда - XSLeaks (Часть 2)
Powered by GitBook
On this page
  • Clickjacking Attack Principle
  • Defense against Clickjacking
  • Real-world Examples
  • Unpreventable Clickjacking?
  • Заключение
  1. Глава 5 - Другие интересные темы

То, что вы видите, это не то, что вы получаете - Clickjacking

PreviousИнтересная и практичная Cookie BombNextЭксплуатация MIME Sniffing

Last updated 8 months ago

В этой последней главе мы рассмотрим некоторые темы безопасности, которые сложнее классифицировать и охватим более широкий спектр контента.

Сначала давайте взглянем на Clickjacking. Clickjacking — это когда вы думаете, что нажимаете на что-то на Веб-сайте A, но на самом деле вы нажимаете на что-то на Веб-сайте B. Ваш клик "угоняется" с Веб-сайта A на Веб-сайт B.

Какой импакт может создать простой клик?

Предположим, что страница, на которую вы нажимаете, — это страница банковского перевода, и ваш номер счета и сумма уже заполнены. Все, что нужно, — это одно нажатие кнопки, чтобы перевести деньги. Это может быть очень опасно (это всего лишь пример, но он иллюстрирует, почему для переводов необходима дополнительная проверка).

Или давайте возьмем более распространенный пример. Предположим, есть страница, которая выглядит как страница отмены подписки на рассылку. Вы нажимаете кнопку "Подтвердить отмену", но под ней на самом деле находится кнопка "Нравится" на Facebook. Таким образом, вы не только не отменили подписку, но и невольно поставили лайк чему-то. Этот тип атаки также известен как Likejacking.

Теперь давайте углубимся в этот метод атаки!

Clickjacking Attack Principle

Принцип Clickjacking заключается в наложении двух веб-страниц, где пользователь видит Веб-сайт A, но кликает на Веб-сайт B.

В более технических терминах это достигается путем встраивания Веб-сайта B с помощью iframe с прозрачностью 0.001, а затем наложения его на содержимое Веб-сайта A с помощью CSS.

Мне кажется наиболее интересным и понятным объяснять Clickjacking через примеры. Обратите внимание на GIF ниже:

Некоторым людям этот пример может показаться слишком простым, и в реальных приложениях такие простые атаки, которые требуют всего лишь одного нажатия кнопки, могут быть редкостью. Возможно, на более сложных веб-сайтах пользователю потребуется сначала ввести какую-то информацию?

В следующем примере Clickjacking разработан для функции "Изменить электронную почту". В отличие от предыдущего примера, где вся веб-страница была покрыта, в этом примере намеренно оставляется ввод с оригинальной веб-страницы, а все остальное накрывается с помощью CSS. Часть с кнопкой использует pointer-events:none, чтобы события могли проходить сквозь.

Веб-страница выглядит как страница для ввода информации о подписке на электронную почту, но после нажатия "Подтвердить" появляется сообщение "Изменение электронной почты успешно", потому что за ней на самом деле находится веб-страница для изменения электронной почты:

Процесс атаки Clickjacking можно кратко описать следующим образом:

  1. Встраивание целевой веб-страницы в контролируемую веб-страницу (с использованием iframes или аналогичных тегов).

  2. Использование CSS на контролируемой веб-странице для наложения целевой веб-страницы, делая её невидимой для пользователя.

  3. Перенаправление пользователя на контролируемую веб-страницу и побуждение его выполнять действия (такие как ввод или клики).

  4. Запуск функций целевой веб-страницы для достижения атаки.

Таким образом, сложность атаки зависит от того, насколько хорошо спроектирована контролируемая веб-страница и сколько взаимодействия требует целевая веб-страница. Например, клик по кнопке гораздо проще, чем ввод информации.

Также стоит отметить, что для проведения такого типа атаки пользователь должен уже быть авторизован на целевом веб-сайте. Пока целевая веб-страница может быть встроена в контролируемую веб-страницу, существует риск Clickjacking.

Defense against Clickjacking

Как уже упоминалось, если веб-страница не может быть встроена в другую веб-страницу, риск Clickjacking отсутствует. Это основное решение для защиты от Clickjacking.

В общем, существует два типа защиты от Clickjacking. Один из них — использование JavaScript для проверки, а другой — информирование браузера через заголовки ответа о том, может ли веб-страница быть встроена.

Frame Busting

Один из методов, называемый Frame Busting, заключается в использовании JavaScript для проверки, как я уже упоминал. Принцип прост, и код тоже:

if (top !== self) {  top.location = self.location}

Каждая веб-страница имеет свой собственный объект window, и window.self ссылается на своё собственное окно. top ссылается на верхнее окно, которое можно рассматривать как верхний уровень окна всей вкладки браузера.

Если веб-страница открыта независимо, top и self будут указывать на одно и то же окно. Однако если веб-страница встроена в iframe, top будет ссылаться на окно, использующее iframe.

Рассмотрим пример. Предположим, у меня есть index.html на localhost, который содержит следующий код:

<iframe src="https://example.com"></iframe>
<iframe src="https://huli.tw"></iframe>

Схема взаимоотношений будет выглядеть так:

Зеленый и желтый представляют две веб-страницы, загруженные в iframes, которые являются двумя разными окнами. Если вы получите доступ к top внутри этих веб-страниц, он будет ссылаться на объект окна localhost/index.html.

Поэтому, проверяя if (top !== self), вы можете определить, помещена ли веб-страница внутри iframe. Если это так, вы можете изменить top.location, чтобы перенаправить верхнюю веб-страницу куда-то еще.

Это звучит отлично и, кажется, не имеет проблем, но его можно обойти с помощью атрибута sandbox у iframes.

У iframe есть атрибут, называемый sandbox, который ограничивает функциональность iframe. Если вы хотите убрать ограничения, вы должны явно указать их. Существует множество возможных значений, но я перечислю несколько:

  1. allow-forms - Разрешает отправку форм.

  2. allow-scripts - Разрешает выполнение JavaScript.

  3. allow-top-navigation - Разрешает изменение top location.

  4. allow-popups - Разрешает всплывающие окна.

Другими словами, если я загружу iframe так:

<iframe src="./busting.html" sandbox="allow-forms">

Даже если busting.html имеет защиту, о которой я упоминал ранее, она не сработает, потому что у неё нет allow-scripts, и JavaScript не может быть выполнен. Однако пользователи все равно могут отправлять формы как обычно.

<style>
html{display:none;}
</style>
<script>   
if (self == top) {       
document.documentElement.style.display = 'block';    
} else {       
top.location = self.location;    
}
</script>

Сначала скрываем всю веб-страницу, которая может быть открыта только при выполнении JavaScript. Таким образом, если вы заблокируете выполнение скриптов с помощью упомянутого sandbox, вы увидите только пустую страницу.

Если вы не используете sandbox, проверка JavaScript не сработает, и вы все равно увидите пустую страницу. Хотя это может обеспечить более полную защиту, у этого метода есть и недостатки. Недостаток в том, что если пользователи добровольно отключат JavaScript, они не увидят ничего.

Таким образом, для пользователей, которые отключают JavaScript, опыт будет довольно плохим. Когда Clickjacking впервые появился в 2008 году, не существовало таких методов защиты, поэтому нам приходилось использовать эти обходные решения. Теперь браузеры имеют улучшенные методы защиты от встраивания веб-страниц.

X-Frame-Options

Этот заголовок может иметь три следующих значения:

  1. DENY

  2. SAMEORIGIN

  3. ALLOW-FROM https://example.com/

Первое значение отклоняет любую веб-страницу от встраивания этой веб-страницы, включая теги <iframe>, <frame>, <object>, <applet>, или <embed>.

Второе значение позволяет делать это только веб-страницам с тем же Origin, а последнее значение позволяет встраивание только из определенных Origin. Кроме того, никакое встраивание не разрешается (можно указать только одно значение, поэтому, если требуется несколько Origin, их нужно динамически настраивать на сервере, аналогично заголовкам CORS).

RFC специально упоминает, что определение последних двух значений может отличаться от ожидаемого, и реализация в каждом браузере может варьироваться.

Например, некоторые браузеры могут проверять только "родительский" и "верхний" уровни, а не проверять каждый уровень. Что означает "уровень"?

Потому что теоретически iframe может иметь бесконечное количество уровней, таких как A встраивает B, B встраивает C, C встраивает D и так далее.

Если мы представим эти отношения в текстовом виде, это будет выглядеть так:

example.com/A.html --> attacker.com --> example.com/B.html --> example.com/target.html

Для самой внутренней target.html, если браузер проверяет только родительский уровень (B.html) и верхний уровень (A.html), то даже если он установлен на X-Frame-Options: SAMEORIGIN, проверка пройдет, потому что эти два уровня действительно имеют один и тот же Origin. Однако на самом деле между ними находится веб-страница атакующего, поэтому все еще существует риск атаки.

Кроме того, есть вторая проблема с X-Frame-Options, которая заключается в плохой поддержке ALLOW-FROM. На данный момент, в 2023 году, основные браузеры не поддерживают директиву ALLOW-FROM.

Первоначальная буква X в X-Frame-Options указывает на то, что это скорее временное решение. В современных браузерах его функциональность была заменена на Content Security Policy (CSP), которая также решает упомянутые ранее проблемы.

CSP: frame-ancestors

CSP имеет директиву под названием frame-ancestors, которую можно установить следующим образом:

  1. frame-ancestors 'none'

  2. frame-ancestors 'self'

  3. frame-ancestors https://a.example.com https://b.example.com

Эти три опции соответствуют предыдущим директивам X-Frame-Options: DENY, SAMEORIGIN и ALLOW-FROM (с поддержкой нескольких источников на этот раз).

Давайте проясним потенциальную путаницу: поведение, ограниченное frame-ancestors, такое же, как и у X-Frame-Options, то есть "какие веб-страницы могут встраивать меня с помощью iframe". С другой стороны, правило CSP frame-src определяет "какие источники могут загружаться на моей веб-странице".

Например, если я установлю frame-src: 'none' в index.html, любая веб-страница, загруженная внутри iframe в index.html, будет заблокирована, независимо от её собственных настроек.

В общем, для успешного отображения iframe обе стороны должны согласиться; если одна из сторон не согласна, это приведет к сбою.

Кроме того, стоит отметить, что frame-ancestors — это правило, поддерживаемое только в CSP уровня 2, которое постепенно принимается основными браузерами, начиная с конца 2014 года.

Defense Summary

Из-за различного уровня поддержки рекомендуется использовать как X-Frame-Options, так и frame-ancestors CSP. Если вы не хотите, чтобы ваша веб-страница загружалась внутри iframe, не забудьте добавить следующие заголовки HTTP-ответа:

X-Frame-Options: DENY 
Content-Security-Policy: frame-ancestors 'none'

Если вы разрешаете загрузку только с тем же Origin, установите это как:

X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'

Если вы хотите указать список разрешенных Origin's, используйте:

X-Frame-Options: ALLOW-FROM https://example.com/
Content-Security-Policy: frame-ancestors https://example.com/

Наконец, есть еще один механизм защиты, который уже реализован в браузерах. Можете ли вы вспомнить, что это?

Это куки по умолчанию SameSite=Lax! С их помощью веб-страницы, встроенные в iframes, не будут отправлять куки на сервер, тем самым не выполняя предварительное условие для атак Clickjacking, которое заключается в том, что "пользователь должен быть авторизован". С этой точки зрения, помимо упомянутого ранее CSRF, куки Same-Site также решают многие другие проблемы безопасности.

Real-world Examples

Yelp

Один из отчетов обсуждал страницу бронирования ресторана. После входа на страницу личная информация пользователя автоматически заполняется, и он может успешно сделать бронирование, нажав кнопку. Таким образом, целью Clickjacking является эта кнопка бронирования.

Каковы последствия, если пользователь неосознанно нажимает кнопку бронирования? Во-первых, злоумышленник может зарегистрировать ресторан и:

  1. Увидеть информацию о людях, которые сделали бронирование, и украсть их адреса электронной почты.

  2. Чтобы отменить бронирование, необходимо заплатить сбор за отмену.

Даже не регистрируя ресторан, все равно можно атаковать. Например, если мне не нравится определенный ресторан, я могу намеренно поделиться их страницей бронирования и создать много поддельных бронирований, что затруднит ресторану определение настоящих заказов.

Twitter

Эта уязвимость довольно интересна и использует проблемы реализации браузера, упомянутые ранее.

В этом случае Twitter уже установил X-Frame-Options: SAMEORIGIN и Content-Security-Policy: frame-ancestors 'self'. Однако во время проверки реализации в некоторых браузерах проверялся только верхний уровень окна на соответствие.

Другими словами, если это было twitter.com => attacker.com => twitter.com, проверка пройдет, позволяя встраивать веб-страницы.

Более того, эта уязвимость возникла в ленте Twitter, поэтому она могла достичь эффекта, похожего на червя. После Clickjacking она отправляла твиты, которые видели бы больше людей, что приводило к тому, что больше людей отправляли бы те же твиты.

Эта ошибка была вызвана проблемами совместимости. Веб-страница установила только X-Frame-Options: ALLOW-FROM без установки CSP, что неэффективно, потому что современные браузеры не поддерживают ALLOW-FROM. Влияние, которое это может оказать, заключается в том, что на сайте есть кнопка "Deactivate Account", которая может ввести пользователей в заблуждение и заставить их нажать на нее без их ведома.

Решение простое: просто используйте frame-ancestors CSP, который поддерживается современными браузерами.

Tumblr

Я специально выбрал этот случай, потому что это цепочка атак!

Ранее существовал тип уязвимости, называемый Self-XSS, при котором только пользователь мог инициировать XSS. Поэтому многие программы BugBounty не принимают этот тип уязвимости, потому что он имеет небольшое влияние.

Этот отчет сочетает Self-XSS с Clickjacking, позволяя пользователям инициировать Self XSS через Clickjacking, что делает атаку легче осуществимой и более реальной.

Как работает эта цепочка?

Сначала пользователю предлагается нажать кнопку, тайно копируя полезную нагрузку XSS в фоновом режиме. Затем пользователя просят вставить ее в другое поле ввода и нажать другую кнопку. Это поле ввода на самом деле является полем для имени пользователя, а последняя кнопка — "Update Data". Следуя инструкциям, пользователь неосознанно изменяет свое имя пользователя на полезную нагрузку XSS.

Это некоторые практические примеры, связанные с Clickjacking. Стоит отметить, что некоторые проблемы вызваны проблемами совместимости, а не неправильными настройками, поэтому правильная конфигурация также важна.

Unpreventable Clickjacking?

Защита от Clickjacking в основном заключается в том, чтобы не позволять другим встраивать вашу веб-страницу. Но что делать, если цель веб-страницы — позволить другим встраивать её? Что следует делать в случае виджетов, таких как виджет Facebook, который включает кнопки "Like" и "Share", предназначенные для встраивания с помощью iframes?

Согласно этим двум статьям:

Полученная информация может в настоящее время лишь немного ухудшить пользовательский опыт в обмен на безопасность. Например, после нажатия кнопки появится всплывающее окно для подтверждения, что добавляет еще один клик для пользователя, но также избегает риска Likejacking.

В качестве альтернативы, я предполагаю, что это поведение также может зависеть от источника веб-сайта. Например, на более авторитетных веб-сайтах это всплывающее окно может не появляться.

Если Likejacking успешен, нажатие кнопки вызовет "Like" страницы Facebook Developer Plugin (я успешно тестировал это сам). Вы можете попробовать это и затем нажать "Show Original page", чтобы увидеть, что находится под кнопкой, а также убрать "Like" со страницы.

Заключение

По сравнению с тем временем, когда поддержка браузеров была не такой полной, сейчас нам гораздо лучше. Браузеры внедрили все больше и больше функций безопасности и новых заголовков ответа, чтобы защитить пользователей от атак. Хотя Clickjacking стал все более трудным для осуществления с появлением куки по умолчанию Same-Site, все же важно помнить о необходимости установки X-Frame-Options и CSP, упомянутых в статье. В конце концов, так работает кибербезопасность: наличие дополнительного уровня защиты всегда полезно.

Дополнительные ссылки:

Я думал, что нажал "Да" и отменил подписку на электронную почту, но на самом деле я нажал "Удалить аккаунт". Это и есть Clickjacking. Если вы хотите испытать это на себе, вы можете попробовать на этой веб-странице: .

Веб-версия, с которой можно поэкспериментировать, также доступна по ссылке: .

Поэтому кто-то предложил более практический подход, улучшив существующий метод (код взят из: ):

Этот заголовок HTTP-ответа был впервые реализован в IE8 в 2009 году, и другие браузеры последовали его примеру. Он стал полноценным в 2013.

Другой пример: если мой index.html установлен на frame-src: , но example.com имеет установленный frame-ancestors: 'none', index.html все равно не сможет загрузить example.com внутри iframe, потому что это будет отклонено с другой стороны.

В 2018 году hk755a сообщил о двух уязвимостях Clickjacking в Yelp, крупнейшем сайте отзывов о ресторанах в США. Уязвимости были названы: и

Сначала давайте рассмотрим уязвимость, о которой сообщил filedescriptor в Twitter в 2015 году: .

Запись автора отличная, но блог недоступен. Вот архив:

Еще один отчет был представлен eo420 в Periscope, дочерней компании Twitter, в 2019 году: .

В 2020 году fuzzme сообщил о уязвимости в Tumblr:.

Я создал простую демонстрационную веб-страницу:

clickjacking example
Advanced clickjacking example
Wikipedia - Framekiller
RFC7034
https://example.com
ClickJacking on IMPORTANT Functions of Yelp
CRITICAL-CLICKJACKING at Yelp Reservations Resulting in exposure of victim Private Data (Email info) + Victim Credit Card MissUse.
Highly wormable clickjacking in player card
Google YOLO
Twitter Periscope Clickjacking Vulnerability
[api.tumblr.com] Exploiting clickjacking vulnerability to trigger self DOM-based XSS
Clickjacking Attack on Facebook: How a Tiny Attribute Can Save the Corporation
Facebook like button click
https://aszx87410.github.io/demo/clickjacking/like.html
TOPCLICKJACKING.md
Clickjacking Defense Cheat Sheet
CSP frame-ancestors