Frame Buster Buster… código de buster necessário

422

Digamos que você não queira que outros sites "enquadrem" seu site em <iframe>:

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

Então você insere JavaScript anti-framing e frame-busting em todas as suas páginas:

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

Excelente! Agora você "quebra" ou interrompe automaticamente qualquer iframe contendo. Exceto por um pequeno problema.

Como se vê, seu código de interrupção de quadros pode ser impedido , como mostrado aqui :

<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>

Este código faz o seguinte:

  • incrementa um contador toda vez que o navegador tenta navegar para fora da página atual, por meio do window.onbeforeunloadmanipulador de eventos
  • configura um timer que dispara a cada milissegundo via setInterval()e, se o contador for incrementado, altera o local atual para um servidor de controle do invasor
  • esse servidor exibe uma página com o código de status HTTP 204 , que não faz com que o navegador navegue em qualquer lugar

Minha pergunta é - e isso é mais um quebra-cabeça de JavaScript do que um problema real - como você pode derrotar o imbecil que destrói os quadros?

Eu tive alguns pensamentos, mas nada funcionou nos meus testes:

  • tentar limpar o onbeforeunloadevento via onbeforeunload = nullnão teve efeito
  • adicionar e alert()interromper o processo avisa o usuário que isso está acontecendo, mas não interfere no código; clicar em OK permite que a interrupção continue normalmente
  • Não consigo pensar em nenhuma maneira de limpar o setInterval()temporizador

Eu não sou muito programador de JavaScript, então aqui está o meu desafio para você: ei, imbecil, você pode bloquear o imbecil?

Jeff Atwood
fonte
6
Não tenho certeza se o frame-buster-buster realmente funciona ... quando tento testá-lo (redirecionando para um manipulador que configurei para retornar um 204), ele me impede de navegar em qualquer lugar fora da página - incluindo digitar coisas na barra de endereço! Preciso fechar a guia do navegador e abrir uma nova para chegar a qualquer lugar. Então, em outras palavras, não tenho certeza se isso precisa de uma solução, porque o quadro-imbecil-imbecil que deseja ser preso é ... preso no início. :) (Ou isso ou eu estraguei o meu teste, que nunca poderia acontecer ...);)
Matt Winckler
16
Matt, o código frame-buster-buster publicado acima definitivamente funciona. Um .. uh .. amigo .. meu .. me contou .. sobre isso. Ou alguma coisa. :)
Jeff Atwood
10
Jeff, você está testando com as duas janelas no mesmo domínio? Parece que você está porque, se não estivesse, as restrições de segurança o impediriam de modificar 'onBeforeUnload'
James
29
Observação: ao postar exemplos, use domínios como example.orgo especificado na RFC 2606 ietf.org/rfc/rfc2606.txt
Christoph
3
Em relação ao tema geral de contra-contra-contramedidas: galactanet.com/comic/view.php?strip=209
Joey

Respostas:

149

Não tenho certeza se isso é viável ou não - mas se você não pode quebrar o quadro, por que não exibir apenas um aviso. Por exemplo, se sua página não for a "página superior", crie um método setInterval que tente quebrar o quadro. Se, após 3 ou 4 tentativas, sua página ainda não estiver na página inicial - crie um elemento div que cubra a página inteira (caixa modal) com uma mensagem e um link como ...

Você está visualizando esta página em uma janela de quadro não autorizado - (Blá blá ... problema de segurança em potencial)

clique neste link para corrigir este problema

Não é o melhor, mas não vejo como eles conseguiram sair dessa.

Hugoware
fonte
2
Eu tentei isso e isso funciona. Outra parte que eu gosto sobre esta solução é que ela traz à luz o usuário em que tipo de site ele estava antes de acessar seu conteúdo. Código de exemplo: if (parent.frames.length> 0) {top.location.replace (document.location); setTimeout (function () {if (parent.frames.length> 0) {document.location = " google.com ";}}, 10); }
papa
Essa não é apenas uma boa maneira de evitar abusos, é bastante amigável para sites que podem querer criar um nome para o seu site apenas para dar uma olhada, mas não para permitir o uso. Idealmente, acho que uma captura de tela da página inicial do site deve ser usada, com algumas explicações sobre por que não pode ser usada no iframe sobreposto na parte superior.
wheresrhys
33
É assim que o Facebook faz isso.
Shamittomar
2
mas talvez isso possa ser explorado se o site bloqueado, por sua vez, criar um falso anti-anti-anti ... (não sei quanto anti-somos até agora) div do lighbox por apresentar um link de phishing ou qualquer outra coisa ... tbc
yunzen
7
Outra idéia seria apenas para wipeout página completamente com algo como document.write("");(depois de ter estabelecido que está sendo enquadrado
gabeio
211

FWIW, a maioria dos navegadores atuais oferece suporte à diretiva X-Frame-Options: negar , que funciona mesmo quando o script está desativado.

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

EricLaw
fonte
6
excelente, apoiar isso no navegador .exe é o caminho a percorrer sem dúvida. Quando você diz "a maioria dos navegadores", quais especificamente? Não consigo encontrar boas fontes para nada além do IE8.
Jeff Atwood
2
Aqui está uma página de teste: enhanie.com/test/clickjack . O Chrome 4.1.249.1042 suporta. Opera 10.50 suporta. O Firefox 3.6.2 ainda não suporta. O Safari 4.0.3 suporta.
EricLaw
1
O Firefox 3.6.9 o suporta de forma nativa ( hackademix.net/2010/08/31/… ) e qualquer instalação do Firefox com NoScript o possui desde o início de 2009 ( hackademix.net/2009/01/29/x-frame- Opções-no-Firefox )
ssokolow 31/08
4
É melhor combinar isso com o framebuster javascript.
Jesse Weigert
1
Ou combine isso com a resposta de dezembro de 12 nesta página: stackoverflow.com/a/13708510/328397
goodguys_activate
34

Usamos a seguinte abordagem em um de nossos sites em 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>
Caçador de cavernas
fonte
3
Bingo! Não sei por que isso não é upvoted mais uma vez que é a melhor resposta (ao lado da resposta X-Frame-Options, mas é melhor para combinar)
Jesse Weigert
1
Essa resposta ou a minha deve ser selecionado :)
Eu gosto da sua idéia de usar isso e as Opções de quadro X: negar resposta.
Max Oeste
Isso requer JS, que é um IMHO bastante grande.
Navin
1
+1 - Esta é a melhor resposta quando X-FRAME-OPTIONS não pode ser usado. (Por exemplo, quando você precisa para permitir ou negar dependendo de referência condicionalmente.)
Jay Sullivan
29

Achei isso e parece funcionar pelo menos no Firefox e no navegador Opera.

if(top != self) {
 top.onbeforeunload = function() {};
 top.location.replace(self.location.href);
}
Jani Hartikainen
fonte
2
a solução de Jani e Jeff (uma vez editada) está correta e funciona de maneira equivalente; dando Jani a aceitar porque sua solução funcionou direito, sem qualquer edição
Jeff Atwood
29
Isso funcionará apenas se as duas janelas forem do mesmo domínio; uma ocorrência rara quando você deseja escapar de um quadro.
James
Se houver quadros aninhados envolvidos, você precisará percorrer a cadeia de quadros e remover todos os onbeforeunloadmanipuladores, não apenas o que está em cima!
Christoph
12
esclarecimentos importantes: isso funcionou para mim porque o iframe src = estava sendo definido dinamicamente e, portanto, a política entre domínios NÃO estava em vigor. JP está absolutamente certo, em um src estático = isso não funcionaria.
9119 Jeff Atwood
5
ok agora alguém pode criar um quadro imbecil imbecil imbecil?
Epaga 12/06/09
23

Considerando o padrão HTML5 atual que introduziu a sandbox para iframe, todos os códigos de interrupção de quadros fornecidos nesta página podem ser desativados quando o invasor usa a sandbox porque restringe o iframe do seguinte:

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

Consulte: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#attr-iframe-sandbox

Agora, considere o invasor usado o seguinte código para hospedar seu site no iframe:

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

Então, todo o código de bloqueio de quadro JavaScript falhará.

Depois de verificar todo o código de barramento de quadro, somente essa defesa funciona em todos os casos:

<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>

proposto originalmente por Gustav Rydstedt, Elie Bursztein, Dan Boneh e Collin Jackson (2010)


fonte
19

Depois de ponderar isso por um tempo, acredito que isso mostrará a eles quem é o chefe ...

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

Usar _topcomo parâmetro de destino para window.open()iniciará na mesma janela.

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

fonte
6

Vou ser corajoso e jogar meu chapéu no anel neste (antigo como é), ver quantos votos negativos posso coletar.

Aqui está minha tentativa, que parece funcionar em todos os lugares em que a testei (Chrome20, IE8 e 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);
}());

Coloquei esse código no <head>e o chamei no final do <body>para garantir que minha página seja renderizada antes de começar a discutir com o código malicioso, não sei se essa é a melhor abordagem, YMMV.

Como funciona?

... eu ouço você perguntar - bem, a resposta honesta é: eu não realmente sei. Demorou muito tempo para fazê-lo funcionar em todos os lugares que eu estava testando, e o efeito exato que isso varia um pouco, dependendo de onde você o executa.

Aqui está o pensamento por trás disso:

  • Defina uma função para executar no intervalo mais baixo possível. O conceito básico por trás de qualquer uma das soluções realistas que eu já vi é preencher o agendador com mais eventos do que o frame buster-buster possui.
  • Sempre que a função for acionada, tente alterar a localização do quadro superior. Requisito bastante óbvio.
  • Programe também uma função para ser executada imediatamente, o que levará muito tempo para ser concluído (impedindo assim o imbecil-quadro de interferir na mudança de local). Escolhi um XMLHttpRequest síncrono porque é o único mecanismo que consigo pensar que não requer (ou pelo menos solicita) interação do usuário e não reduz o tempo de CPU do usuário.

Para o meu http://mysite.tld/page-that-takes-a-while-to-load(o destino do XHR), usei um script PHP parecido com este:

<?php sleep(5);

O que acontece?

  • Chrome e Firefox esperam 5 segundos enquanto o XHR é concluído e, em seguida, redirecionam com sucesso para o URL da página emoldurada.
  • O IE redireciona praticamente imediatamente

Você não pode evitar o tempo de espera no Chrome e Firefox?

Aparentemente não. No começo, apontei o XHR para uma URL que retornaria um 404 - isso não funcionou no Firefox. Depois, tentei a sleep(5);abordagem que acabei encontrando para obter essa resposta e comecei a brincar com a duração do sono de várias maneiras. Não consegui encontrar um padrão real para o comportamento, mas descobri que, se for muito curto, especificamente o Firefox não jogará bola (o Chrome e o IE parecem ser bastante bem-comportados). Não sei qual é a definição de "muito curto" em termos reais, mas cinco segundos parecem funcionar sempre.


Se algum ninjas Javascript de passagem quiser explicar um pouco melhor o que está acontecendo, por que isso é (provavelmente) errado, não confiável, o pior código que eles já viram etc.

DaveRandom
fonte
Parece que você pode remover todas as suas frases preocupantes
mplungjan 29/03
6

A partir de 2015, você deve usar a frame-ancestorsdiretiva do CSP2 para isso. Isso é implementado através de um cabeçalho de resposta HTTP.

por exemplo

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

Obviamente, muitos navegadores não oferecem suporte ao CSP2, portanto, é aconselhável incluir o X-Frame-Optionscabeçalho antigo :

X-Frame-Options: DENY

Eu recomendaria incluir os dois de qualquer maneira, caso contrário, seu site continuaria vulnerável a ataques de Clickjacking em navegadores antigos e, é claro, você obteria um enquadramento indesejável, mesmo sem intenção maliciosa. A maioria dos navegadores é atualizada automaticamente atualmente, no entanto, você ainda tende a impedir que os usuários corporativos fiquem presos nas versões antigas do Internet Explorer por motivos de compatibilidade de aplicativos herdados.

SilverlightFox
fonte
1
Todos os principais navegadores agora oferecem suporte ao CSP. Esta é a resposta correta em 2019 e o futuro previsível.
Stephen R
5

Ok, então sabemos que estavam em um quadro. Então localizamos.href para outra página especial com o caminho como uma variável GET. Agora, explicamos ao usuário o que está acontecendo e fornecemos um link com a opção target = "_ TOP". É simples e provavelmente funcionaria (ainda não foi testado), mas requer alguma interação do usuário. Talvez você possa apontar o site ofensivo para o usuário e fazer uma espécie de vergonha de clickers para o seu site em algum lugar .. Apenas uma idéia, mas funciona de noite ..


fonte
5

Todas as soluções propostas forçam diretamente uma alteração na localização da janela superior. E se um usuário quiser que o quadro esteja lá? Por exemplo, o quadro superior nos resultados da imagem dos mecanismos de pesquisa.

Escrevi um protótipo em que, por padrão, todas as entradas (links, formulários e elementos de entrada) estão desativadas e / ou não fazem nada quando ativadas.

Se um quadro contendo for detectado, as entradas serão desativadas e uma mensagem de aviso será exibida na parte superior da página. A mensagem de aviso contém um link que abrirá uma versão segura da página em uma nova janela. Isso impede que a página seja usada para o clickjacking, enquanto ainda permite que o usuário visualize o conteúdo em outras situações.

Se nenhum quadro contendo for detectado, as entradas serão ativadas.

Aqui está o código. Você precisa definir os atributos HTML padrão para valores seguros e adicionar atributos adicionais que contenham os valores reais. Provavelmente está incompleto e, para total segurança, atributos adicionais (penso nos manipuladores de eventos) provavelmente terão que ser tratados da mesma maneira:

<!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>
Johan Stuyts
fonte
O problema é que o criador de molduras poderia usar a posição: absoluto para colocar o botão ativo em cima dos botões inativos, e o usuário verá sua página da web e pensará que está clicando em SEUS botões.
21138 jmucchiello
A mensagem de aviso ainda seria exibida, mas é claro que é fácil cobrir o link para a página segura, como você sugere. Mas por que todo o trabalho de enquadrar minha página para que as pessoas cliquem em um botão familiar, se você pode simplesmente copiar a página e obter o mesmo efeito? O código acima impede principalmente o clickjacking. Se você mostrar minha página invisivelmente acima de outra página, não é possível invocar ações no meu site.
23610 Johan Stuyts
Se isso for colocado em um quadro de zona restrita do IE8 ou em um sandbox do Chrome, o Javascript nunca será executado. Gostaria de saber quais modificações são necessárias nesses casos
goodguys_activate
4

Bem, você pode modificar o valor do contador, mas essa é obviamente uma solução quebradiça. Você pode carregar seu conteúdo via AJAX depois de determinar que o site não está dentro de um quadro - também não é uma ótima solução, mas espero evitar o acionamento do evento on beforeunload (suponho).

Edit: Outra idéia. Se você detectar que está em um quadro, peça ao usuário para desativar o javascript, antes de clicar em um link que o leve ao URL desejado (passar uma string de consulta que informe à sua página para informar ao usuário que ele pode reativar o javascript quando existem).

Editar 2: Seja nuclear - se você detectar que está em um quadro, basta excluir o conteúdo do corpo do documento e imprimir alguma mensagem desagradável.

Edit 3: Você pode enumerar o documento principal e definir todas as funções como nulas (mesmo as anônimas)?

RedFilter
fonte
O Outlook (anteriormente Hotmail) 'fica nuclear' se não conseguir sair de um quadro - coloca todo o conteúdo do <body>interior de uma <plaintext>marca definida display: none. É bastante eficaz.
uınbɐɥs
4

Se você adicionar um alerta logo após o código do buster, o alerta interromperá o encadeamento javascript e deixará a página carregar. É isso que o StackOverflow faz, e ele sai dos meus iframes, mesmo quando eu uso o buster de frame-busting. Também funcionou com minha página de teste simples. Isso foi testado apenas no Firefox 3.5 e IE7 no Windows.

Código:

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

Eu acho que você estava quase lá. Você tentou:

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

ou alternativamente:

window.parent.prevent_bust = 0;

Nota: Na verdade, eu não testei isso.

Jeff Meatball Yang
fonte
1
Editei seu exemplo de código (o teste para o pai parece falhar), mas a versão editada parece funcionar!
9119 Jeff Atwood
1
Legal. É sempre complicado responder com código não testado - eu faço isso pelo menos para transmitir a ideia - e deixar o pobre solicitante debugar. :)
Jeff Meatball Yang
12
Não funcionará se o pai estiver em um domínio diferente, o que provavelmente é o caso!
21139 Josh Stodola
2

Que tal chamar o imbecil repetidamente também? Isso criará uma condição de corrida, mas pode-se esperar que o imbecil saia por cima:

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

Se você observar os valores retornados por setInterval()eles, geralmente têm um dígito, então você pode desativar todas essas interrupções com uma única linha de código:

for (var j = 0 ; j < 256 ; ++j) clearInterval(j)
Robin Nixon
fonte
2

Eu só poderia ter conseguido uma maneira de quebrar o javascript do frame buster buster. Usando o getElementsByName na minha função javascript, defini um loop entre o frame buster e o script do frame buster buster. confira este post. http://www.phcityonweb.com/frame-buster-buster-buster-2426

Phcityonweb
fonte
0

setInterval e setTimeout criam um intervalo de incremento automático. Cada vez que setTimeout ou setInterval é chamado, esse número aumenta em um, de modo que, se você chamar setTimeout, obterá o valor mais alto atual.

   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";
   }

Como é quase inédito que haja 10000 setIntervals e setTimeouts simultâneos funcionando, e como setTimeout retorna "último intervalo ou tempo limite criado + 1", e como top.clearInterval ainda está acessível, isso derrotará os ataques de chapéu preto no quadro sites descritos acima.

cwallenpoole
fonte
0

Use o htaccess para evitar o conjunto de quadros de alto desempenho, iframe e qualquer conteúdo como imagens.

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

Isso mostrará uma página de direitos autorais em vez da esperada.

BF
fonte
Isso depende do referenciador que a) nem sempre está definido (devido às configurações ou extensões do navegador ou simplesmente porque a página de referência está usando HTTPS sem usar <meta name="referrer" …/>eb) também definido ao clicar em links, para que você também não permita links para sua página e quebre a teia.
Martin