Кадр Buster Buster... необходим код борца

Скажем, вы не хотите, чтобы другие сайты "разорвали" ваш сайт в <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, так что вот моя задача для вас: Эй, бэттер, можете ли вы разбить бюст?

Ответ 1

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

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

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

Не лучшее, но я не вижу, как они могли бы script выйти из этого.

Ответ 2

FWIW, большинство современных браузеров поддерживают директиву X-Frame-Options: deny, которая работает даже когда script отключен.

IE8:
http://blogs.msdn.com/ie/archive/2009/01/27/ie8-security-part-vii-clickjacking-defenses.aspx

Firefox (3.6.9)
https://bugzilla.mozilla.org/show_bug.cgi?id=475530
https://developer.mozilla.org/en/The_X-FRAME-OPTIONS_response_header

Chrome/Webkit
http://blog.chromium.org/2010/01/security-in-depth-new-security-features.html
http://trac.webkit.org/changeset/42333

Ответ 3

Мы использовали следующий подход на одном из наших веб-сайтов: 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>

Ответ 4

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

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

Ответ 5

Учитывая текущий стандарт 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)

Ответ 6

Обдумав это ненадолго, я верю, что это покажет им, кто босс...

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

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

Ответ 7

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

Ответ 8

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

Вот моя попытка, которая, кажется, работает везде, где я ее тестировал (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-ниндзя хотят объяснить немного лучше, что происходит, почему это (возможно) неправильно, ненадежно, худший код, который они когда-либо видели и т.д. Я с радостью слушаю.

Ответ 9

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

Ответ 10

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

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

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

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

Вот код. Вам необходимо установить стандартные атрибуты 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>

Ответ 11

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

например.

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

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

X-Frame-Options: DENY

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

Ответ 12

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

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

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

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

Ответ 13

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

код:

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

Ответ 14

Я думаю, ты был почти там. Вы пробовали:

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

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

window.parent.prevent_bust = 0;

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

Ответ 15

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

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

Ответ 16

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

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

Ответ 17

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

Ответ 18

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 все еще доступен, это победит черную шляпу атаки на веб-сайты кадров, которые описаны выше.

Ответ 19

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

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

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

Ответ 20

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