Frame Buster Buster… нужен код ошибки


422

Допустим, вы не хотите, чтобы другие сайты «вставляли» ваш сайт в <iframe>:

<iframe src="http://example.org"></iframe>

Таким образом, вы вставляете антикадровый код JavaScript во все ваши страницы:

/* break us out of any containing iframes */
if (top != self) { top.location.replace(self.location.href); }

Отлично! Теперь вы автоматически «перебираете» или выбрасываете любой содержащий iframe. За исключением одной маленькой проблемы.

Как выясняется, ваш код перебора кадров может быть отключен , как показано здесь :

<script type="text/javascript">
    var prevent_bust = 0  
    window.onbeforeunload = function() { prevent_bust++ }  
    setInterval(function() {  
      if (prevent_bust > 0) {  
        prevent_bust -= 2  
        window.top.location = 'http://example.org/page-which-responds-with-204'  
      }  
    }, 1)  
</script>

Этот код делает следующее:

  • увеличивает счетчик каждый раз, когда браузер пытается отойти от текущей страницы через window.onbeforeunloadобработчик событий
  • устанавливает таймер, который срабатывает каждую миллисекунду с помощью setInterval(), и, если он видит счетчик увеличен, меняет текущее местоположение на сервер управления атакующего
  • этот сервер обслуживает страницу с кодом состояния HTTP 204 , который не заставляет браузер перемещаться в любом месте

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

У меня было несколько мыслей, но в моем тестировании ничего не получалось:

  • попытка очистить onbeforeunloadсобытие через onbeforeunload = nullничего не дала
  • добавление alert()остановленного процесса позволяет пользователю знать, что это происходит, но никак не влияет на код; нажатие кнопки OK позволяет продолжить процесс как обычно
  • Я не могу придумать способ очистить setInterval()таймер

Я не очень-то программист на JavaScript, поэтому вот мой вызов для вас: эй, бастер, можешь ли ты уничтожить бастера?


6
Я не уверен , что каркасно-бустер-бустер на самом деле работает ... когда я пытаюсь проверить его (перенаправление обработчика я создал для возврата 204), он мне мешает навигации в любом месте за пределами страницы - в том числе набрав вещи в адресной строке! Мне нужно закрыть вкладку браузера и открыть новую, чтобы попасть куда угодно. Другими словами, я не уверен, что для этого нужно решение, потому что фрейм-бастер, желающий быть убитым, ... с самого начала разбит. :) (Или это, или я испортил свой тест, который никогда не мог случиться ...);)
Мэтт Винклер

16
Мэтт, вышеприведенный код «фреймбастер-бастер» определенно работает. А ... эээ ... мой друг ... сказал мне ... об этом. Или что-то. :)
Джефф Этвуд

10
Джефф, вы тестируете с обоими окнами в одном домене? Похоже, что вы, потому что, если вы не были, то ограничения безопасности помешали бы вам изменить 'onBeforeUnload'
Джеймс

29
Примечание: при публикации примеров используйте домены, example.orgкак указано в RFC 2606. Ietf.org/rfc/rfc2606.txt
Кристоф

3
Относительно общей темы контр-контрмер: galactanet.com/comic/view.php?strip=209
Джои

Ответы:


149

Я не уверен, является ли это жизнеспособным или нет - но если вы не можете разбить кадр, почему бы просто не отобразить предупреждение. Например, если ваша страница не является «верхней страницей», создайте метод setInterval, который пытается разбить фрейм. Если после 3 или 4 попыток ваша страница все еще не является верхней страницей - создайте элемент div, который покрывает всю страницу (модальное поле) сообщением и ссылкой, например ...

Вы просматриваете эту страницу в неавторизованном рамочном окне - (бла-бла ... потенциальная проблема безопасности)

нажмите на эту ссылку, чтобы решить эту проблему

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


2
Я пробовал это, и это работает. Еще одна вещь, которая мне нравится в этом решении, заключается в том, что он показывает пользователю, на каком сайте он был, прежде чем перейти к вашему контенту. Пример кода: if (parent.frames.length> 0) {top.location.replace (document.location); setTimeout (function () {if (parent.frames.length> 0) {document.location = " google.com ";}}, 10); }
Папа

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

33
Вот как это делает Facebook.
Шамитомар

2
но, возможно, это можно было бы использовать, если сайт разрыва в свою очередь создаст ложное анти-анти-анти ... (не знаю, насколько анти-мы до сих пор) Lighbox Div для себя представляет фишинговую ссылку или что-то еще ... tbc
yunzen

7
Другая идея состоит в том, чтобы просто стереть страницу полностью чем-то вроде document.write("");(после того, как вы установили, что это подставлено
gabeio

211

6
отлично, поддерживая это в браузере .exe - это путь без сомнения. Когда вы говорите «большинство браузеров», какие конкретно? Я не могу найти хорошие источники ни для чего, кроме IE8.
Джефф Этвуд

2
Вот тестовая страница: gainie.com/test/clickjack . Chrome 4.1.249.1042 поддерживает. Opera 10.50 поддерживает. Firefox 3.6.2 пока не поддерживает. Safari 4.0.3 поддерживает.
EricLaw

1
Firefox 3.6.9 будет поддерживать его изначально ( hackademix.net/2010/08/31/… ), и любая установка Firefox с NoScript имела его с начала 2009 года ( hackademix.net/2009/01/29/x-frame- options-in-firefox )
ssokolow

4
Лучше всего сочетать это с фреймбастером javascript.
Джесси Вейгерт

1
Или совместите это с ответом за декабрь '12 на этой странице: stackoverflow.com/a/13708510/328397
goodguys_activate

34

Мы использовали следующий подход на одном из наших сайтов по адресу http://seclab.stanford.edu/websec/framebusting/framebust.pdf

<style>
 body { 
 display : none   
}
</style>
<script>
if(self == top) {
document.getElementsByTagName("body")[0].style.display = 'block';
}
else{
top.location = self.location;
}
</script>

3
Бинго! Не уверен, почему за это больше не проголосуют, так как это лучший ответ (рядом с ответом X-Frame-Options, но лучше объединить оба)
Джесси Вейгерт,

1
Этот ответ или мой должен быть выбран :)

Мне нравится ваша идея использовать и этот, и X-Frame-Options: отказать в ответе.
Макс Вест

Это требует JS, что является довольно большим бременем IMHO.
Навин

1
+1 - это лучший ответ, когда X-FRAME-OPTIONS нельзя использовать. (Например, когда вам нужно условно разрешить или запретить в зависимости от реферера.)
Джей Салливан

29

Придумал это, и, похоже, работает, по крайней мере, в Firefox и браузере Opera.

if(top != self) {
 top.onbeforeunload = function() {};
 top.location.replace(self.location.href);
}

2
оба решения Джани и Джеффа (после их редактирования) верны и работают одинаково; Яни согласился, потому что его решение работало правильно, без редактирования
Джефф Этвуд

29
Это будет работать, только если два окна принадлежат одному домену; редкое явление, когда вы хотите сбежать из кадра.
Джеймс

Если задействованы вложенные фреймы, вам придется пройтись по цепочке фреймов и удалить все onbeforeunloadобработчики, а не только один сверху!
Кристоф

12
Важное уточнение: это сработало для меня, потому что iframe src = устанавливался динамически, и поэтому междоменная политика НЕ ​​действовала. JP абсолютно прав, в статическом src = это не сработает.
Джефф Этвуд

5
Хорошо, теперь может кто-нибудь придумать Buster Buster Frame Buster?
Epaga

23

Принимая во внимание текущий стандарт HTML5, который ввел «песочницу» для iframe, все коды очистки фреймов, представленные на этой странице, могут быть отключены, когда злоумышленник использует «песочницу», потому что это ограничивает iframe следующим:

allow-forms: Allow form submissions.
allow-popups: Allow opening popup windows.
allow-pointer-lock: Allow access to pointer movement and pointer lock.
allow-same-origin: Allow access to DOM objects when the iframe loaded form same origin
allow-scripts: Allow executing scripts inside iframe
allow-top-navigation: Allow navigation to top level window

Пожалуйста, смотрите: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#attr-iframe-sandbox

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

<iframe src="URI" sandbox></iframe>

Тогда весь код разрыва фрейма JavaScript потерпит неудачу.

После проверки всех кадровых кодов кадров только эта защита работает во всех случаях:

<style id="antiClickjack">body{display:none !important;}</style>
<script type="text/javascript">
   if (self === top) {
       var antiClickjack = document.getElementById("antiClickjack");
       antiClickjack.parentNode.removeChild(antiClickjack);
   } else {
       top.location = self.location;
   }
</script>

что первоначально предложено Густавом Ридстедтом, Эли Бурштейном, Даном Бонех и Коллином Джексоном (2010)


19

Подумав немного, я верю, что это покажет им, кто в доме хозяин ...

if(top != self) {
  window.open(location.href, '_top');
}

Использование _topв качестве целевого параметра для window.open()запускает его в том же окне.


6
if (top != self) {
  top.location.replace(location);
  location.replace("about:blank"); // want me framed? no way!
}

6

Я собираюсь быть смелым и бросить свою шляпу в кольцо на этом (старое как оно есть), посмотрите, сколько отрицательных голосов я могу собрать.

Вот моя попытка, которая, кажется, работает везде, где я ее тестировал (Chrome20, IE8 и FF14):

(function() {
    if (top == self) {
        return;
    }

    setInterval(function() {
        top.location.replace(document.location);
        setTimeout(function() {
            var xhr = new XMLHttpRequest();
            xhr.open(
                'get',
                'http://mysite.tld/page-that-takes-a-while-to-load',
                false
            );
            xhr.send(null);
        }, 0);
    }, 1);
}());

Я поместил этот код в <head>и вызвал его в конце, <body>чтобы убедиться, что моя страница отображается, прежде чем она начнет спорить со злонамеренным кодом, не знаю, является ли это лучшим подходом, YMMV.

Как это работает?

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

Вот мысль за этим:

  • Установите функцию для запуска с минимально возможным интервалом. Основная концепция любого из реалистичных решений, которые я видел, состоит в том, чтобы заполнить планировщик большим количеством событий, чем имеет Buster-Buster.
  • Каждый раз, когда функция срабатывает, попробуйте изменить местоположение верхней рамки. Довольно очевидное требование.
  • Также запланируйте немедленное выполнение функции, которая займет много времени (таким образом, блокируя работу противодействия нарушению местоположения). Я выбрал синхронный XMLHttpRequest, потому что это единственный механизм, о котором я могу думать, который не требует (или, по крайней мере, не требует) взаимодействия с пользователем и не затрачивает процессорное время пользователя.

Для моего http://mysite.tld/page-that-takes-a-while-to-load(цель XHR) я использовал скрипт PHP, который выглядит следующим образом:

<?php sleep(5);

Что просходит?

  • Chrome и Firefox ждут 5 секунд, пока XHR завершит работу, а затем успешно перенаправят на URL страницы в рамке.
  • IE перенаправляет почти сразу

Разве вы не можете избежать времени ожидания в Chrome и Firefox?

Очевидно нет. Сначала я указал XHR на URL, который вернул бы 404 - это не сработало в Firefox. Затем я попробовал sleep(5);подход, который я в конечном итоге получил для этого ответа, затем я начал играть с продолжительностью сна различными способами. Я не мог найти никакой реальной картины поведения, но обнаружил, что если она слишком короткая, то Firefox не будет играть в мяч (Chrome и IE, похоже, ведут себя довольно хорошо). Я не знаю, что такое определение «слишком короткий» в реальном выражении, но 5 секунд, кажется, работает каждый раз.


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


Кажется, вы можете удалить все ваши тревожные предложения
mplungjan

6

С 2015 года вы должны использовать CSP2 frame-ancestors для этого директиву . Это реализовано через заголовок ответа HTTP.

например

Content-Security-Policy: frame-ancestors 'none'

Конечно, не многие браузеры поддерживают CSP2, поэтому стоит добавить старый X-Frame-Optionsзаголовок:

X-Frame-Options: DENY

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


1
Все основные браузеры теперь поддерживают CSP. Это правильный ответ в 2019 году и в обозримом будущем.
Стивен Р

5

Итак, мы знаем, что были в кадре. Таким образом, мы location.href переходим к другой специальной странице с путем в качестве переменной GET. Теперь мы объясним пользователю, что происходит, и предоставим ссылку с параметром target = "_ TOP". Это просто и, вероятно, сработает (еще не проверял), но требует некоторого взаимодействия с пользователем. Может быть, вы могли бы указать на сайт оскорбительного пользователя и сделать так, чтобы где-нибудь на вашем сайте было много позоров ... Просто идея, но это работает ночью ..


5

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

Я написал прототип, в котором по умолчанию все входы (ссылки, формы и элементы ввода) отключены и / или ничего не делают при активации.

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

Если содержащий кадр не обнаружен, входы активируются.

Вот код Необходимо установить стандартные атрибуты HTML на безопасные значения и добавить дополнительные атрибуты, которые содержат фактические значения. Вероятно, он неполон, и для полной безопасности дополнительные атрибуты (я думаю о обработчиках событий), вероятно, придется обрабатывать аналогичным образом:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
  <head>
    <title></title>
    <script><!--
      function replaceAttributeValuesWithActualOnes( array, attributeName, actualValueAttributeName, additionalProcessor ) {
        for ( var elementIndex = 0; elementIndex < array.length; elementIndex += 1 ) {
          var element = array[ elementIndex ];
          var actualValue = element.getAttribute( actualValueAttributeName );
          if ( actualValue != null ) {
            element[ attributeName ] = actualValue;
          }

          if ( additionalProcessor != null ) {
            additionalProcessor( element );
          }
        }
      }

      function detectFraming() {
        if ( top != self ) {
          document.getElementById( "framingWarning" ).style.display = "block";
        } else {
          replaceAttributeValuesWithActualOnes( document.links, "href", "acme:href" );

          replaceAttributeValuesWithActualOnes( document.forms, "action", "acme:action", function ( form ) {
            replaceAttributeValuesWithActualOnes( form.elements, "disabled", "acme:disabled" );
          });
        }
      }
      // -->
    </script>
  </head>
  <body onload="detectFraming()">
    <div id="framingWarning" style="display: none; border-style: solid; border-width: 4px; border-color: #F00; padding: 6px; background-color: #FFF; color: #F00;">
      <div>
        <b>SECURITY WARNING</b>: Acme App is displayed inside another page.
        To make sure your data is safe this page has been disabled.<br>
        <a href="framing-detection.html" target="_blank" style="color: #090">Continue working safely in a new tab/window</a>
      </div>
    </div>
    <p>
      Content. <a href="#" acme:href="javascript:window.alert( 'Action performed' );">Do something</a>
    </p>
    <form name="acmeForm" action="#" acme:action="real-action.html">
      <p>Name: <input type="text" name="name" value="" disabled="disabled" acme:disabled=""></p>
      <p><input type="submit" name="save" value="Save" disabled="disabled" acme:disabled=""></p>
    </form>
  </body>
</html>

Проблема в том, что создатель фреймов может использовать position: absolute для размещения активной кнопки поверх ваших неактивных кнопок, и пользователь просто увидит вашу веб-страницу и подумает, что он нажимает ВАШИ кнопки.
jmucchiello

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

Если это помещено в рамку зоны ограничения IE8 или рамку песочницы Chrome, Javascript никогда не будет работать. Интересно, какие модификации нужны в этих случаях
goodguys_activate

4

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

Изменить: еще одна идея. Если вы обнаружите, что находитесь в кадре, попросите пользователя отключить JavaScript, прежде чем щелкнуть ссылку, которая приведет вас к нужному URL-адресу (передав строку запроса, позволяющую вашей странице сообщить пользователю, что он может повторно включить JavaScript, когда они здесь).

Редактировать 2: Идите ядерным путем - если вы обнаружите, что находитесь в рамке, просто удалите содержимое тела документа и напечатайте какое-нибудь неприятное сообщение.

Редактировать 3: Можете ли вы перечислить верхний документ и установить все функции на ноль (даже анонимные)?


Outlook (ранее Hotmail) «становится ядерным», если не может выйти из фрейма - он помещает весь контент <body>внутри <plaintext>тега в значение display: none. Это довольно эффективно.
2012 года

4

Если вы добавите предупреждение сразу после кода ошибки, тогда предупреждение остановит поток JavaScript и позволит странице загрузиться. Это то, что делает StackOverflow, и он вылетает из моих фреймов, даже когда я использую бастер. Это также работало с моей простой тестовой страницей. Это было проверено только в Firefox 3.5 и IE7 на Windows.

Код:

<script type="text/javascript">
if (top != self){
  top.location.replace(self.location.href);
  alert("for security reasons bla bla bla");
}
</script>

3

Я думаю, что вы были почти там. Ты пытался:

window.parent.onbeforeunload = null;
window.parent.location.replace(self.location.href);

или, альтернативно:

window.parent.prevent_bust = 0;

Примечание: я на самом деле не проверял это.


1
Я отредактировал ваш пример кода (похоже, что тест для родителя не прошел), но отредактированная версия, похоже, работает!
Джефф Этвуд

1
Круто. Всегда непросто ответить непроверенным кодом - я делаю это, чтобы, по крайней мере, донести идею - и позволить отладчику плохого аскера. :)
Джефф Фрикадель Ян

12
Не будет работать, если родитель находится в другом домене, что вполне вероятно!
Джош Стодола

2

А как насчет вызова бастера, а также несколько раз? Это создаст условия гонки, но можно надеяться, что бастер выйдет победителем:

(function() {
    if(top !== self) {
        top.location.href = self.location.href;
        setTimeout(arguments.callee, 0);
    }
})();

2

Если вы посмотрите на значения, возвращаемые setInterval()как правило, они состоят из одной цифры, то есть вы можете отключить все такие прерывания одной строкой кода:

for (var j = 0 ; j < 256 ; ++j) clearInterval(j)

2

Возможно, я только что получил способ уничтожить javascript. Используя getElementsByName в моей функции javascript, я установил петлю между обработчиком кадров и реальным сценарием уничтожения кадров. проверить этот пост. http://www.phcityonweb.com/frame-buster-buster-buster-2426


0

setInterval и setTimeout создают автоматически увеличивающийся интервал. Каждый раз, когда вызывается setTimeout или setInterval, это число увеличивается на единицу, поэтому, если вы вызовете setTimeout, вы получите текущее максимальное значение.

   var currentInterval = 10000;
   currentInterval += setTimeout( gotoHREF, 100 );
   for( var i = 0; i < currentInterval; i++ ) top.clearInterval( i );
   // Include setTimeout to avoid recursive functions.
   for( i = 0; i < currentInterval; i++ )     top.clearTimeout( i );

   function gotoHREF(){
           top.location.href = "http://your.url.here";
   }

Поскольку почти 10000 одновременных операций setIntervals и setTimeouts работают почти всегда, а setTimeout возвращает «последний созданный интервал или время ожидания + 1», а поскольку top.clearInterval по-прежнему доступен, это отразит атаки черной шляпы на кадр. сайты, которые описаны выше.


0

Используйте htaccess, чтобы избежать большого набора кадров, iframe и любого контента, например изображений.

RewriteEngine on
RewriteCond %{HTTP_REFERER} !^http://www\.yoursite\.com/ [NC]
RewriteCond %{HTTP_REFERER} !^$
RewriteRule ^(.*)$ /copyrights.html [L]

Это покажет страницу авторских прав вместо ожидаемого.


Это зависит от реферера, который а) не всегда устанавливается (из-за настроек или расширений браузера или просто потому, что ссылающаяся страница использует HTTPS без использования <meta name="referrer" …/>и б) также устанавливается при переходе по ссылкам, поэтому вы также запрещаете ссылки на свою страницу и прерываете паутина.
Мартин
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.