Frame Buster Buster ... codice buster necessario


422

Supponiamo che non desideri che altri siti "modellino" il tuo sito in un <iframe>:

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

Quindi inserisci JavaScript anti-framing e anti-frame in tutte le tue pagine:

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

Eccellente! Ora "rompi" o rompi automaticamente qualsiasi iframe contenente. Tranne un piccolo problema.

A quanto pare, il tuo codice di frame-busting può essere eliminato , come mostrato qui :

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

Questo codice esegue le seguenti operazioni:

  • incrementa un contatore ogni volta che il browser tenta di spostarsi dalla pagina corrente, tramite il window.onbeforeunloadgestore eventi
  • imposta un timer che si attiva ogni millisecondo setInterval()e, se vede incrementare il contatore, cambia la posizione corrente in un server di controllo dell'attaccante
  • quel server serve una pagina con codice di stato HTTP 204 , che non fa navigare il browser da nessuna parte

La mia domanda è - e si tratta più di un puzzle JavaScript che di un problema reale - come si può sconfiggere il busto del frame-busting?

Ho avuto alcuni pensieri, ma nulla ha funzionato nei miei test:

  • il tentativo di cancellare l' onbeforeunloadevento tramite onbeforeunload = nullnon ha avuto alcun effetto
  • l'aggiunta di un alert()processo interrotto informa l'utente che stava accadendo, ma non ha interferito in alcun modo con il codice; facendo clic su OK, il busting continua normalmente
  • Non riesco a pensare ad alcun modo per azzerare il setInterval()timer

Non sono un programmatore JavaScript, quindi ecco la mia sfida per te: hey buster, riesci a sballare il frame-busting buster?


6
Non sono sicuro che il frame-buster-buster funzioni davvero ... quando provo a provarlo (reindirizzandolo a un gestore che ho impostato per restituire un 204), mi impedisce di navigare ovunque al di fuori della pagina - inclusa la scrittura di cose nella barra degli indirizzi! Devo chiudere la scheda del browser e aprirne una nuova per arrivare ovunque. Quindi, in altre parole, non sono sicuro che questo abbia bisogno di una soluzione, perché il frame-buster-buster che vuole essere beccato è ... per cominciare. :) (O quello o ho rovinato il mio test, che non potrebbe mai accadere ...);)
Matt Winckler,

16
Matt, il codice frame-buster-buster pubblicato sopra funziona sicuramente . Un ... amico ... mio ... mi ha detto ... a riguardo. O qualcosa. :)
Jeff Atwood,

10
Jeff, stai testando con entrambe le finestre sullo stesso dominio? Sembra che tu lo sia perché se non lo fossi, le restrizioni di sicurezza ti impedirebbero di modificare "onBeforeUnload"
James

29
Nota a margine: quando pubblichi esempi, utilizza domini come example.orgspecificato in RFC 2606 ietf.org/rfc/rfc2606.txt
Christoph,

3
Per quanto riguarda il tema generale delle contromisure contromisure: galactanet.com/comic/view.php?strip=209
Joey,

Risposte:


149

Non sono sicuro che questo sia fattibile o meno, ma se non riesci a rompere il frame, perché non visualizzare semplicemente un avviso. Ad esempio, se la tua pagina non è la "pagina principale", crea un metodo setInterval che tenti di rompere il frame. Se dopo 3 o 4 tentativi la tua pagina non è ancora la pagina principale, crea un elemento div che copra l'intera pagina (casella modale) con un messaggio e un link come ...

Stai visualizzando questa pagina in una finestra non autorizzata - (Blah blah ... potenziale problema di sicurezza)

fai clic su questo link per risolvere questo problema

Non è il migliore, ma non vedo in alcun modo che possano uscirne.


2
Ho provato questo e questo funziona. Un altro pezzo che mi piace di questa soluzione è che mette in luce l'utente che tipo di sito si trovava prima di andare al tuo contenuto. Codice di esempio: if (parent.frames.length> 0) {top.location.replace (document.location); setTimeout (function () {if (parent.frames.length> 0) {document.location = " google.com ";}}, 10); }
papa,

Questo non è solo un buon modo per evitare gli abusi, ma è anche abbastanza amichevole per i siti che potrebbero voler creare il tuo sito solo per dare un'occhiata, anche se non per consentirne l'uso. Idealmente, penso che dovrebbe essere usato uno screenshot della home page del sito, con alcune spiegazioni del perché non può essere utilizzato nell'iframe sovrapposto in alto.
wheresrhys,

33
Ecco come lo fa Facebook.
Shamittomar,

2
ma forse questo potrebbe essere sfruttato se il sito di busting a sua volta creerà un falso anti anti anti ... (non so quanto anti siamo finora) lighbox div per se stesso presentando un link di phishing o qualunque altra cosa ... da confermare
yunzen

7
Un'altra idea sarebbe quella di cancellare completamente la pagina con qualcosa del genere document.write("");(dopo aver stabilito che è stata inquadrata
gabeio

211

6
eccellente, supportando questo nel browser .exe è la strada da percorrere senza dubbio. Quando dici "la maggior parte dei browser", quali in particolare? Non riesco a trovare buone fonti per niente tranne IE8.
Jeff Atwood,

2
Ecco una pagina di prova: enhanie.com/test/clickjack . Chrome 4.1.249.1042 supporta. Supporti Opera 10.50. Firefox 3.6.2 NON supporta ancora. Safari 4.0.3 supporta.
EricLaw,

1
Firefox 3.6.9 lo supporterà in modo nativo ( hackademix.net/2010/08/31/… ) e qualsiasi installazione di Firefox con NoScript lo ha avuto dall'inizio del 2009 ( hackademix.net/2009/01/29/x-frame- options-in-firefox )
ssokolow il

4
È meglio combinarlo con il framebuster javascript.
Jesse Weigert,

1
O combinalo con la risposta di dicembre '12 in questa pagina: stackoverflow.com/a/13708510/328397
goodguys_activate

34

Abbiamo utilizzato il seguente approccio in uno dei nostri siti Web da 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
Bingo! Non sono sicuro del perché questo non sia più votato poiché è la risposta migliore (accanto alla risposta Opzioni X-Frame, ma è meglio combinare entrambi)
Jesse Weigert,

1
Questa o la mia risposta dovrebbero essere selezionate :)

Mi piace la tua idea di utilizzare sia questo che X-Frame-Options: negare la risposta.
Max West,

Ciò richiede JS, che è un grosso onere IMHO.
Navin,

1
+1 - Questa è la risposta migliore quando non è possibile utilizzare X-FRAME-OPTIONS. (Ad esempio, quando è necessario consentire o negare condizionatamente in base al referrer.)
Jay Sullivan,

29

Venne con questo, e sembra funzionare almeno in Firefox e nel browser Opera.

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

2
sia la soluzione di Jani che quella di Jeff (una volta modificata) sono corrette e funzionano in modo equivalente; dare l'accettazione a Jani perché la sua soluzione ha funzionato senza modifiche
Jeff Atwood,

29
Funzionerà solo se le due finestre sono dello stesso dominio; un evento raro quando si desidera fuggire da una cornice.
James,

Se sono coinvolti frame nidificati, dovrai percorrere la catena dei frame e rimuovere tutti i onbeforeunloadgestori, non solo quello in alto!
Christoph,

12
chiarimento importante: questo ha funzionato per me perché iframe src = veniva impostato in modo dinamico e quindi la politica tra domini NON era in vigore. JP ha assolutamente ragione, in un src statico = questo non funzionerebbe.
Jeff Atwood,

5
ok ora qualcuno può inventare un frame buster buster buster buster?
Epaga

23

Considerando l'attuale standard HTML5 che ha introdotto sandbox per iframe, tutti i codici di busting dei frame forniti in questa pagina possono essere disabilitati quando l'attaccante utilizza sandbox perché limita l'iframe a seguire:

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

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

Ora, considera l'attaccante usato il seguente codice per ospitare il tuo sito in iframe:

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

Quindi, tutto il codice di busting del frame JavaScript non riuscirà.

Dopo aver verificato tutto il codice di frame busing, funziona solo questa difesa in tutti i casi:

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

quello originariamente proposto da Gustav Rydstedt, Elie Bursztein, Dan Boneh e Collin Jackson (2010)


19

Dopo aver riflettuto per un po ', credo che questo mostrerà loro chi è il capo ...

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

L'utilizzo _topcome parametro target per window.open()lo avvierà nella stessa finestra.


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

6

Ho intenzione di essere coraggioso e gettare il mio cappello sul ring su questo (così com'è), vedere quanti voti negativi posso raccogliere.

Ecco il mio tentativo, che sembra funzionare ovunque l'ho testato (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);
}());

Ho inserito questo codice nel <head>e lo ho chiamato dalla fine del <body>per assicurarsi che la mia pagina sia resa prima che inizi a discutere con il codice maligno, non so se questo è l'approccio migliore, YMMV.

Come funziona?

... ti sento chiedere - beh, la risposta onesta è, non proprio so . Ci sono voluti molti tentativi per farlo funzionare ovunque stavo testando, e l'effetto esatto che ha varia leggermente a seconda di dove lo si esegue.

Ecco il pensiero dietro di esso:

  • Impostare una funzione per l'esecuzione all'intervallo più basso possibile. Il concetto alla base di una qualsiasi delle soluzioni realistiche che ho visto è quello di riempire lo scheduler con più eventi di quanti ne abbia il frame buster-buster.
  • Ogni volta che la funzione viene attivata, prova a cambiare la posizione del riquadro superiore. Requisito abbastanza ovvio.
  • Pianifica anche una funzione da eseguire immediatamente che richiederà molto tempo per il completamento (impedendo così al buster-buster del telaio di interferire con il cambio di posizione). Ho scelto un XMLHttpRequest sincrono perché è l'unico meccanismo a cui riesco a pensare che non richiede (o almeno richiede) l'interazione dell'utente e non mastica il tempo di CPU dell'utente.

Per il mio http://mysite.tld/page-that-takes-a-while-to-load(il bersaglio dell'XHR) ho usato uno script PHP che assomiglia a questo:

<?php sleep(5);

Che succede?

  • Chrome e Firefox attendono i 5 secondi mentre l'XHR è completo, quindi reindirizzano correttamente all'URL della pagina incorniciata.
  • IE reindirizza praticamente immediatamente

Non puoi evitare i tempi di attesa in Chrome e Firefox?

Apparentemente no. All'inizio ho indicato l'XHR a un URL che restituiva un 404 - questo non funzionava in Firefox. Quindi ho provato l' sleep(5);approccio che alla fine ho ottenuto per questa risposta, quindi ho iniziato a giocare con la lunghezza del sonno in vari modi. Non sono riuscito a trovare un modello reale per il comportamento, ma ho scoperto che se è troppo corto, in particolare Firefox non giocherà a palla (Chrome e IE sembrano essere abbastanza ben educati). Non so quale sia la definizione di "troppo breve" in termini reali, ma 5 secondi sembra funzionare ogni volta.


Se qualche ninja Javascript di passaggio vuole spiegare un po 'meglio cosa sta succedendo, perché questo è (probabilmente) sbagliato, inaffidabile, il peggior codice che abbia mai visto ecc. Ascolterò felicemente.


Sembra che tu possa rimuovere tutte le tue preoccupanti frasi
mplungjan

6

A partire dal 2015, è necessario utilizzare la frame-ancestorsdirettiva CSP2 per questo. Questo è implementato tramite un'intestazione di risposta HTTP.

per esempio

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

Naturalmente, non molti browser supportano ancora CSP2, quindi è consigliabile includere la vecchia X-Frame-Optionsintestazione:

X-Frame-Options: DENY

Consiglierei di includerli entrambi, altrimenti il ​​tuo sito continuerebbe a essere vulnerabile agli attacchi Clickjacking nei vecchi browser e, naturalmente, otterrai frame indesiderati anche senza intenzioni dannose. La maggior parte dei browser si aggiorna automaticamente al giorno d'oggi, tuttavia si tende comunque a bloccare gli utenti aziendali su vecchie versioni di Internet Explorer per motivi di compatibilità delle applicazioni legacy.


1
Tutti i principali browser ora supportano CSP. Questa è la risposta corretta nel 2019 e nel prossimo futuro.
Stephen R,

5

Ok, quindi sappiamo che erano in una cornice. Quindi location.href passa a un'altra pagina speciale con il percorso come variabile GET. Spieghiamo ora all'utente cosa sta succedendo e forniamo un collegamento con un'opzione target = "_ TOP". È semplice e probabilmente funzionerebbe (non l'ho testato), ma richiede una certa interazione da parte dell'utente. Forse potresti segnalare all'utente il sito offensivo e creare una sorta di vergogna di click jackers sul tuo sito da qualche parte .. Solo un'idea, ma è notte di lavoro ..


5

Tutte le soluzioni proposte impongono direttamente un cambiamento nella posizione della finestra in alto. Cosa succede se un utente desidera che il frame sia presente? Ad esempio il frame superiore nei risultati delle immagini dei motori di ricerca.

Ho scritto un prototipo in cui per impostazione predefinita tutti gli input (collegamenti, moduli ed elementi di input) sono disabilitati e / o non fanno nulla quando attivati.

Se viene rilevato un frame contenente, gli input vengono lasciati disabilitati e viene visualizzato un messaggio di avviso nella parte superiore della pagina. Il messaggio di avviso contiene un collegamento che aprirà una versione sicura della pagina in una nuova finestra. Ciò impedisce che la pagina venga utilizzata per il clickjacking, pur consentendo all'utente di visualizzare i contenuti in altre situazioni.

Se non viene rilevato alcun frame contenente, gli ingressi sono abilitati.

Ecco il codice È necessario impostare gli attributi HTML standard su valori sicuri e aggiungere attributi aggiuntivi che contengono i valori effettivi. Probabilmente è incompleto e per la massima sicurezza gli attributi aggiuntivi (sto pensando ai gestori di eventi) dovranno probabilmente essere trattati allo stesso modo:

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

Il problema è che il creatore di frame potrebbe usare la posizione: assoluto per posizionare il pulsante attivo sopra i pulsanti inattivi e l'utente vedrà solo la tua pagina web e penserà che stiano facendo clic sui TUOI pulsanti.
jmucchiello,

Il messaggio di avviso verrà comunque visualizzato, ma ovviamente è facile coprire il link alla pagina sicura come suggerito. Ma perché affrontare tutta la difficoltà di inquadrare la mia pagina per indurre le persone a fare clic su un pulsante familiare se puoi semplicemente copiare la pagina e ottenere lo stesso effetto? Il codice sopra impedisce principalmente il clickjacking. Se mostri la mia pagina invisibilmente sopra un'altra pagina, non è possibile invocare azioni sul mio sito.
Johan Stuyts,

Se questo viene inserito in un frame di area riservata IE8 o in un frame sandbox di Chrome, Javascript non funzionerà mai. Mi chiedo quali modifiche siano necessarie in questi casi
goodguys_activate il

4

Bene, puoi modificare il valore del contatore, ma questa è ovviamente una soluzione fragile. Puoi caricare i tuoi contenuti tramite AJAX dopo aver stabilito che il sito non si trova all'interno di un frame - inoltre non è un'ottima soluzione, ma si spera che eviti di attivare l'evento on onunload (suppongo).

Modifica: un'altra idea. Se rilevi di essere in un frame, chiedi all'utente di disabilitare javascript, prima di fare clic su un link che ti porta all'URL desiderato (passando una stringa di query che fa sapere alla tua pagina di dire all'utente che possono riattivare javascript una volta che hanno ci sono).

Modifica 2: diventa nucleare: se rilevi di essere in una cornice, basta eliminare il contenuto del corpo del documento e stampare qualche brutto messaggio.

Modifica 3: puoi enumerare il documento principale e impostare tutte le funzioni su null (anche quelle anonime)?


Outlook (ex Hotmail) "diventa nucleare" se non riesce a uscire da un frame: mette l'intero contenuto <body>all'interno di un <plaintext>tag impostato su display: none. È abbastanza efficace.
uınbɐɥs

4

Se aggiungi un avviso subito dopo il codice buster, l'avviso interromperà il thread javascript e consentirà il caricamento della pagina. Questo è ciò che StackOverflow fa, e viene espulso dai miei iframe, anche quando utilizzo il busting del frame busting. Ha funzionato anche con la mia semplice pagina di test. Questo è stato testato solo in Firefox 3.5 e IE7 su Windows.

Codice:

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

3

Penso che tu fossi quasi arrivato. Hai provato:

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

o, in alternativa:

window.parent.prevent_bust = 0;

Nota: in realtà non l'ho provato.


1
Ho modificato il tuo esempio di codice (il test per parent sembra fallire) ma la versione modificata sembra funzionare!
Jeff Atwood,

1
Freddo. È sempre difficile rispondere con un codice non testato - lo faccio almeno per far passare l'idea - e lasciare che il povero aspirante debug. :)
Jeff Meatball Yang,

12
Non funzionerà se parent è su un dominio diverso, il che è probabilmente il caso!
Josh Stodola,

2

Che ne dici di chiamare ripetutamente anche il buster? Questo creerà una condizione di gara, ma si potrebbe sperare che il buster esca in cima:

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

2

Se guardi i valori restituiti da setInterval()quelli di solito sono cifre singole, quindi di solito puoi disabilitare tutti questi interrupt con una singola riga di codice:

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


0

setInterval e setTimeout creano un intervallo di incremento automatico. Ogni volta che viene chiamato setTimeout o setInterval, questo numero aumenta di uno, quindi se si chiama setTimeout, si otterrà il valore corrente più alto.

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

Poiché è quasi inaudito che ci siano 10000 setIntervals e setTimeouts simultanei funzionanti e poiché setTimeout restituisce "ultimo intervallo o timeout creato + 1", e poiché top.clearInterval è ancora accessibile, questo sconfiggerà gli attacchi black hat al frame siti Web descritti sopra.


0

Usa htaccess per evitare set di frame high-jacking, iframe e qualsiasi contenuto come le immagini.

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

Questo mostrerà una pagina di copyright invece del previsto.


Questo si basa sul referrer che è a) non sempre impostato (a causa delle impostazioni o delle estensioni del browser o semplicemente perché la pagina di riferimento utilizza HTTPS senza usare <meta name="referrer" …/>eb) anche quando si fa clic sui collegamenti, quindi si vietano anche i collegamenti alla pagina e si interrompono la rete.
Martin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.