Frame Buster Buster ... необходим код ошибки - PullRequest
406 голосов
/ 06 июня 2009

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

Ответы [ 19 ]

204 голосов
/ 19 марта 2010
145 голосов
/ 18 июня 2009

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

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

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

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

33 голосов
/ 04 декабря 2012

Мы использовали следующий подход на одном из наших сайтов от 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>
29 голосов
/ 06 июня 2009

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

if(top != self) {
 top.onbeforeunload = function() {};
 top.location.replace(self.location.href);
}
22 голосов
/ 17 мая 2013

Учитывая текущий стандарт 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 голосов
/ 18 июня 2009

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

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

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

6 голосов
/ 19 июня 2009
if (top != self) {
  top.location.replace(location);
  location.replace("about:blank"); // want me framed? no way!
}
5 голосов
/ 15 августа 2012

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

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

5 голосов
/ 19 июня 2009

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

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

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

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

Вот код. Вам необходимо установить стандартные атрибуты 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>
5 голосов
/ 12 июня 2009

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

...