Атаки кликджекинга (clickjacking ) и как их предотвратить

Spread the love

Перевод: Andrea Chiarelli – Clickjacking Attacks and How to Prevent Them

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

Что такое кликджекинг?

Цель атаки с использованием кликджекинга – заставить ничего не подозревающих посетителей сайта выполнить действия на другом сайте (целевом сайте). Например, пользователя может привлечь веб-сайт, который обещает ему исключительный приз. Когда пользователь нажимает кнопку, чтобы принять приз, его щелчок вместо этого используется для покупки товара на веб-сайте электронной коммерции. Обычно эта атака выполняется путем сокрытия пользовательского интерфейса целевого веб-сайта и организации видимого пользовательского интерфейса таким образом, чтобы пользователь не знал, что щелкнул целевой веб-сайт. Из-за такого расположения пользовательского интерфейса этот вид атаки также известен как атака UI redressing или UI redress attack.

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

Типы атак кликджекинга

В зависимости от характера конкретной операции подобные атаки могут иметь разные названия. Рассмотрим, например, следующие варианты:

  • Likejacking: Этот вид атаки направлен на захват кликов пользователей и перенаправление их на «лайки» на странице Facebook или других социальных сетях.
  • Cookiejacking: В этом случае пользователь вынужден взаимодействовать с элементом пользовательского интерфейса, например, с помощью перетаскивания и предоставлять злоумышленнику файлы cookie, хранящиеся в его браузере. Таким образом, злоумышленник сможет выполнять действия на целевом веб-сайте от имени пользователя.
  • Filejacking: С помощью этого типа атаки пользователь позволяет злоумышленнику получить доступ к своей локальной файловой системе и взять файлы.
  • Cursorjacking: Этот метод изменяет положение курсора на другое место, отличное от того, где его воспринимает пользователь. Таким образом, пользователь считает, что он совершает одно действие, в то время как на самом деле совершает другое.
  • Password manager attacks: Этот тип атаки направлен на то, чтобы обмануть менеджеры паролей, чтобы воспользоваться их функцией автозаполнения.

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

Clickjacking в действии

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

Чтобы запустить пример приложения, которое мы будем использовать, вам достаточно установить Node.js на вашем компьютере. Однако принципы, лежащие в основе уязвимости кликджекинга, и стратегии предотвращения не зависят от конкретного языка программирования или фреймворка.

Настройка среды

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

git clone https://github.com/auth0-blog/clickjacking-sample-app.git

После завершения загрузки установите зависимости проекта, перейдя в папку проекта и выполнив эту команду:

npm install

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

npm start

Наконец, откройте браузер и перейдите по адресу http://localhost:3000. Вы должны увидеть следующую страницу:

The clickjacking vulnerable website

В проекте реализована конкретная страница фильмов на фиктивном веб-сайте потоковой передачи фильмов. Помимо возможности транслировать фильм, вы также можете купить соответствующий DVD. Однако для просмотра и покупки фильма вам потребуется сеанс с аутентификацией. Чтобы упростить задачу, в этом проекте не реализован фактический процесс аутентификации. Вы можете смоделировать сеанс с аутентификацией, щелкнув ссылку действительного сеанса в сообщении внизу страницы. После получения смоделированного сеанса пользователя это сообщение исчезнет.

Запуск атаки кликджекинга

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

Итак, запустим сайт злоумышленника, выполнив следующую команду в окне терминала:

node attacker-server.js

Затем откройте новую вкладку или новый экземпляр браузера и перейдите по адресу http://localhost:4000. Вы должны увидеть следующую страницу:

The clickjacking attacker's website

Эта страница обещает вам чудесный отдых, просто нажав кнопку Accept the prize! . Кажется, это совершенно не связано с сайтом фильма. Однако, если вы нажмете кнопку, вы фактически покупаете DVD на веб-сайте фильма. Вы можете проверить это, открыв инструмент разработчика в своем браузере и проанализировав сетевой трафик. Это пример отслеживания в Chrome DevTools:

Click capture in action

Вы можете заметить запрос HTTP POST к конечной точке http://localhost:3000/purchase веб-сайта фильма.

Какая связь между этим сайтом и сайтом фильмов?

Анатомия атаки

Чтобы понять, что произошло, заглянем на страницу злоумышленника. Перейдите в папку views и откройте страницу index.ejs. Тело страницы выглядит следующим образом:

<!-- views/index.ejs -->

<html lang=en>
  <!-- ...existing markup... -->
  <body>

    <div id="attacker_website">
      <h1>You Are The Winner!!!</h1>
      <h2>You won an awesome tropics holiday!</h2>
      <img alt="Tropical Holiday" src="images/tropical-holiday.jpg">
      <h2>Accept it by clicking the button below.</h2>
      <button type="submit">Accept the prize!</button>
    </div>      
    
    <iframe id="vulnerable_website" src="http://localhost:3000">
    </iframe>

  </body>
</html>

Его содержимое представляет собой шаблон EJS с двумя основными элементами:

  • Видимая часть страницы определяется блоком div с идентификатором attacker_website .
  • Iframe с идентификатором vulnerable_website указывает на веб-сайт фильма, но на самом деле вы не видите его на странице злоумышленника.

Уловка, соединяющая два веб-сайта, выполняется правилами CSS, определяющими положение и видимость этих элементов. Посмотрим на них:

<!-- views/index.ejs -->

<html lang=en>
  <head>
  <!-- ...existing markup... -->
    <style>
      #vulnerable_website {
        position:relative;
        opacity:0.0;
        width:800px;
        height:900px;
        top:75px;
        left: -95px;
        z-index:2;
        padding-left:80px;
        }

      #attacker_website {
        position:absolute;
        z-index:1;
        }
      #attacker_website button {
        margin-left:100px;
      }
    </style>
  </head>
  <!-- ...existing markup... -->
</html>

Как видите, iframe vulnerable_website присвоил значение 0,0 для своей opacity, что делает его прозрачным. Кроме того, он расположен так, что перекрывает div attacker_website . Вы можете увидеть, как iframe перекрывает div, изменив значение opacity iframe на 0,3. После изменения этого значения перезапустите сайт злоумышленника. Вы должны увидеть страницу злоумышленника, как показано на следующем рисунке:

Overlapping websites for clickjacking attacks

Вы видите, что Accept the prize! Кнопка перекрывает кнопку Buy DVD на веб-сайте фильма.

Свойство z-index завершает работу: невидимый iframe находится над div атакующего. Итак, пользователи думают, что нажимают на кнопку Accept the prize!, но на самом деле они нажимают кнопку Buy DVD.

Теперь, когда вы знаете, как работает атака кликджекинга, должно быть понятно, почему этот метод также известен как UI redressing.

Обратите внимание, что для успешной атаки вам необходимо иметь действующий сеанс на веб-сайте фильма. Однако сама атака не связана с использованием файлов cookie сеанса. Атака может быть проведена даже против веб-сайта, который не требует аутентификации.

Отличия от атаки CSRF

Механизм атаки кликджекинга может быть похож на атаку CSRF, когда злоумышленник отправляет запрос на целевой сервер, используя ваш активный сеанс. Однако это совсем другое.

Фактически, в случае CSRF злоумышленник создает HTTP-запрос и использует сеанс пользователя для отправки его на сервер. В случае кликджекинга пользователь напрямую взаимодействует с целевым веб-сайтом. Атакующий не создает никаких запросов. Запрос, отправленный на целевой сервер, является законным запросом пользователя.

Как предотвратить атаки кликджекинга

Теперь вы знаете, как работают атаки кликджекинга. Давайте рассмотрим, как вы можете предотвратить их и сделать свой сайт безопаснее.

Даже если пример приложения, приведенный в этой статье, является традиционным веб-приложением, учтите, что ядро атаки – это возможность включить любой веб-сайт или приложение в iframe. Это означает, что атака кликджекинга может затронуть любой тип приложения независимо от технологии или инфраструктуры, использованной для его создания. Таким образом, уязвимы не только обычные веб-приложения, но и React, Angular и другие приложения.

То же касается и серверной части приложения.

Защита на стороне клиента

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

Рассмотрите возможность добавления следующего скрипта в заголовок страницы уязвимого веб-сайта:

<!-- templates/index.ejs -->

<!DOCTYPE html>
<html lang=en>
  <head>
    <meta charset=utf-8>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <link rel="stylesheet" type="text/css" href="css/tacit-css.min.css"/>
    <title>The Vulnerable Movie Center</title>
    <!-- 👇 new code -->
    <script>
      if (top != window) {
        top.location = window.location;
      }
    </script> 

  <!-- ...existing markup... -->

</html>

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

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

<!-- views/index.ejs -->

<html lang=en>
  <head>
    <!-- ...existing markup... -->
  </head>
  <body>
    <!-- 👇 new code -->

    <script>
      window.onbeforeunload = function() {
        return false;
      };
    </script>
    <!-- ...existing markup... -->
  </body>
</html>

Обрабатывая событие onbeforeunload, злоумышленник пытается отключить закрытие текущей страницы. Этот подход может не работать в некоторых браузерах, так как может потребоваться подтверждение пользователя. Но у злоумышленника есть другие варианты. Например, они могут использовать атрибут sandbox для iframe, как показано ниже:

<!-- views/index.ejs -->

<html lang=en>
  <head>
    <!-- ...existing markup... -->
  </head>
  <body>
    <!-- ...existing markup... -->
    
    <!-- 👇 changed code -->
    <iframe id="vulnerable_website" 
          src="http://localhost:3000" 
          sandbox="allow-scripts allow-forms allow-same-origin">
    </iframe>
  </body>
</html>

В этом случае злоумышленник указывает, что содержимому iframe разрешено выполнять сценарии (allow-scripts), отправлять формы (allow-forms) и обрабатывать его как принадлежащее к одному источнику (allow-same-origin). Никаких других разрешений не указано, поэтому содержимое iframe не может заменить содержимое верхнего уровня, как это допускало значение allow-top-navigation.

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

Если вы хотите попробовать эту защиту на стороне клиента и нейтрализацию атакующего, вы можете загрузить адаптированный проект с помощью следующей команды:

git clone -b client-side-defenses https://github.com/auth0-blog/clickjacking-sample-app.git

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

Использование заголовка X-Frame-Options

Лучший подход к предотвращению атак с использованием кликджекинга – попросить браузер заблокировать любую попытку загрузить ваш сайт в iframe. Вы можете сделать это, отправив HTTP-заголовок X-Frame-Options.

Для этого отредактируйте файл server.js, как показано ниже:

// server.js

const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');

const port = 3000;
const app = express();

app.set('views', './templates');
app.set('view engine', 'ejs');

//👇 new code
app.use(function(req, res, next) {
  res.setHeader('X-Frame-Options', 'sameorigin');
  next();
});

// ...existing code...

app.listen(port, () => console.log(`The server is listening at http://localhost:${port}`));

Вы настроили Express middleware, которое добавляет HTTP-заголовок X-Frame-Options к каждому ответу браузера. Значение, связанное с этим заголовком ответа, – sameorigin, которое сообщает браузеру, что только страницы одного и того же веб-сайта могут включать эту страницу в iframe.

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

<!-- views/index.ejs -->

<html lang=en>
  <head>
  <!-- ...existing markup... -->
    <style>
      #vulnerable_website {
        position:relative;
        opacity:0.3;      //👈 changed code
        width:800px;
        height:900px;
        top:75px;
        left: -95px;
        z-index:2;
        padding-left:80px;
        }

      #attacker_website {
        position:absolute;
        z-index:1;
        }
      #attacker_website button {
        margin-left:100px;
      }
    </style>
  </head>
  <!-- ...existing markup... -->
</html>

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

Disabling clickjacking attacks with X-Frame-Options header

На этот раз содержимое iframe заблокировано.

Альтернативным значением для заголовка X-Frame-Options является deny, что предотвращает любую попытку поместить страницу во фрейм.

Чтобы попробовать этот подход, вы можете загрузить вариант проекта приложения с помощью следующей команды:

git clone -b x-frame-options https://github.com/auth0-blog/clickjacking-sample-app.git

Использование CSP

Все основные браузеры поддерживают заголовок X-Frame-Options. Однако он никогда не был стандартизирован, поэтому могут быть браузеры, не поддерживающие его. Альтернативный стандартный подход к предотвращению атак типа «кликджекинг» – использование определенных директив политики безопасности контента Content Security Policy (CSP).

Например, после восстановления проекта примера приложения в исходное состояние измените содержимое файла server.js следующим образом:

// server.js

const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');

const port = 3000;
const app = express();

app.set('views', './templates');
app.set('view engine', 'ejs');

//👇 new code
app.use(function(req, res, next) {
  res.setHeader("Content-Security-Policy", "frame-ancestors 'self';");
  next();
});

// ...existing code...

app.listen(port, () => console.log(`The server is listening at http://localhost:${port}`));

В этом случае вы назначаете заголовку Content-Security-Policy значение  frame-ancestors 'self'; для каждого исходящего ответа. Эта директива CSP позволяет получить тот же результат, что и заголовок X-Frame-Options со значением sameorigin .

Альтернативные значения для управления внедрением iframe через заголовок Content-Security-Policy:

  • frame-ancestors 'none'; : Это предотвращает любую попытку включить страницу во фрейм.
  • frame-ancestors https://www.authorized-website.com;: Эта директива позволяет указать, какому веб-сайту разрешено встраивать страницу во фрейм. Вы можете указать несколько URI.

Чтобы протестировать подход CSP для защиты примера приложения от кликджекинга, загрузите проект, выполнив эту команду:

git clone -b content-security-policy https://github.com/auth0-blog/clickjacking-sample-app.git

Использование cookie sameSite origin

Если ваше веб-приложение уязвимо для кликджекинга из-за сеансовых файлов cookie, как в примере приложения, которое поставляется с этой статьей, вы можете защитить его, используя свойство sameSite файлов cookie. В этом случае защита основана не на нарушении поведения iframe, а на предотвращении того, чтобы сеанс был действительным, когда веб-сайт находится в iframe. Учтите, что этот подход не поможет вам, когда действия вашего сайта не зависят от активного сеанса.

Свойство sameSite было введено недавно, поэтому старые браузеры могут его не поддерживать.

Давайте посмотрим, как можно применить этот подход. Восстановите исходный проект, как описано в разделе «Настройка среды», и измените содержимое файла server.js, как показано в следующем фрагменте кода:

// server.js

// ...existing code...

app.use(express.static('public'));
app.use(session({
  secret: 'my-secret',
  resave: true,
  saveUninitialized: true,
  cookie: {
    httpOnly: true,
    sameSite: 'strict'    //👈 new code
  }
}));
app.use(bodyParser.urlencoded({ extended: true }));

// ...existing code...

app.listen(port, () => console.log(`The server is listening at http://localhost:${port}`));

Вы просто добавили свойство sameSite со значением «strict» в файл cookie сеанса. Это значение указывает браузеру не отправлять файл cookie сеанса, если запрос поступает из другого домена.

Чтобы убедиться, что пользователь не может завершить покупку через веб-сайт злоумышленника, вам необходимо получить доступ к веб-сайту злоумышленника по адресу http://127.0.0.1:4000. Это необходимо, потому что вы используете одно и то же доменное имя (localhost) как для уязвимого, так и для злоумышленника, а файлы cookie используются независимо от порта. Конечно, в производственной среде у вас не будет этой проблемы.

Чтобы загрузить проект, реализующий эту защиту от кликджекинга, выполните следующую команду:

git clone -b same-site-cookie https://github.com/auth0-blog/clickjacking-sample-app.git

Заключение

Эта статья познакомила вас с анализом того, как работает кликджекинг-атака, и как вы можете защитить свое веб-приложение, применяя различные подходы. У каждой из предложенных стратегий есть свои преимущества и недостатки. Поскольку первоочередную задачу защиты безопасности можно обойти, лучше всего объединить эти подходы, чтобы в случае сбоя одного из них, скажем, из-за отсутствия поддержки браузера, другая стратегия могла бы добиться успеха.

Для более подробного обсуждения предотвращения кликджекинга вы можете взглянуть на шпаргалку OWASP.

Была ли вам полезна эта статья?
[9 / 4.9]

Spread the love
Подписаться
Уведомление о
guest
0 Комментарий
Inline Feedbacks
View all comments