Frame Buster Buster… code buster nécessaire


422

Supposons que vous ne souhaitiez pas que d'autres sites "cadrent" votre site dans un <iframe>:

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

Ainsi, vous insérez du JavaScript anti-cadrage et anti-trame dans toutes vos pages:

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

Excellent! Maintenant, vous "éclatez" ou sortez de tout iframe contenant automatiquement. Sauf pour un petit problème.

En fin de compte, votre code de suppression de trame peut être supprimé , comme indiqué ici :

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

Ce code effectue les opérations suivantes:

  • incrémente un compteur chaque fois que le navigateur tente de s'éloigner de la page actuelle, via le window.onbeforeunload gestionnaire d'événements
  • configure une minuterie qui se déclenche toutes les millisecondes via setInterval() , et s'il voit le compteur incrémenté, change l'emplacement actuel en serveur du contrôle de l'attaquant
  • ce serveur sert une page avec le code d'état HTTP 204 , ce qui ne fait pas naviguer le navigateur n'importe où

Ma question est - et il s'agit plus d'un puzzle JavaScript que d'un problème réel - comment pouvez-vous vaincre le briseur de casse-trame?

J'ai eu quelques réflexions, mais rien n'a fonctionné dans mes tests:

  • tenter d'effacer l' onbeforeunloadévénement via onbeforeunload = nulln'a eu aucun effet
  • l'ajout d'un alert()arrêt du processus a fait savoir à l'utilisateur que cela se passait, mais n'a pas interféré avec le code en aucune façon; cliquer sur OK permet à l'éclatement de continuer normalement
  • Je ne vois aucun moyen d'effacer le setInterval()chronomètre

Je ne suis pas beaucoup un programmeur JavaScript, alors voici mon défi pour vous: bon buster, pouvez-vous casser le buster casse-trame?


6
Je ne suis pas sûr que le frame-buster-buster fonctionne réellement ... quand j'essaye de le tester (rediriger vers un gestionnaire que j'ai configuré pour renvoyer un 204), il m'empêche de naviguer n'importe où en dehors de la page - y compris en tapant des trucs dans la barre d'adresse! Je dois fermer l'onglet du navigateur et en ouvrir un nouveau pour arriver n'importe où. En d'autres termes, je ne suis pas sûr que cela nécessite une solution, car le cadre-buster-buster qui veut être éclaté est ... éclaté pour commencer. :) (Soit ça, soit j'ai foiré mon test, ce qui ne pourrait jamais arriver ...);)
Matt Winckler

16
Matt, le code frame-buster-buster affiché ci-dessus fonctionne certainement . Un .. euh .. un ami .. à moi .. m'en a parlé .. Ou quelque chose. :)
Jeff Atwood

10
Jeff, testez-vous avec les deux fenêtres sur le même domaine? Il semble que vous l'êtes parce que si ce n'était pas le cas, les restrictions de sécurité vous empêcheraient de modifier «onBeforeUnload»
James

29
Remarque: lors de la publication d'exemples, veuillez utiliser des domaines tels example.orgque spécifiés dans la RFC 2606 ietf.org/rfc/rfc2606.txt
Christoph

3
Concernant le thème général des contre-contre-contre-mesures: galactanet.com/comic/view.php?strip=209
Joey

Réponses:


149

Je ne sais pas si cela est viable ou non - mais si vous ne pouvez pas casser le cadre, pourquoi ne pas simplement afficher un avertissement. Par exemple, si votre page n'est pas la «première page», créez une méthode setInterval qui essaie de casser le cadre. Si après 3 ou 4 essais, votre page n'est toujours pas la première page - créez un élément div qui couvre toute la page (boîte modale) avec un message et un lien comme ...

Vous consultez cette page dans une fenêtre de cadre non autorisée - (Bla bla ... problème de sécurité potentiel)

cliquez sur ce lien pour résoudre ce problème

Pas le meilleur, mais je ne vois pas comment ils pourraient en sortir.


2
J'ai essayé ceci et cela fonctionne. Un autre élément que j'aime dans cette solution est qu'elle met en évidence à l'utilisateur quel type de site il / elle visitait avant d'accéder à votre contenu. Exemple de code: if (parent.frames.length> 0) {top.location.replace (document.location); setTimeout (function () {if (parent.frames.length> 0) {document.location = " google.com ";}}, 10); }
pape

Non seulement c'est un bon moyen d'éviter les abus, mais il est également assez convivial pour les sites qui peuvent souhaiter cadrer votre site simplement pour y jeter un coup d'œil, mais ne pas autoriser son utilisation. Idéalement, je pense qu'une capture d'écran de la page d'accueil du site devrait être utilisée, avec une explication de pourquoi elle ne peut pas être utilisée dans l'iframe superposée en haut.

33
C'est ainsi que Facebook procède.
shamittomar

2
mais peut-être que cela pourrait être exploité si le site de destruction créait à son tour un faux anti anti anti ... (ne sais pas combien d'anti nous sommes jusqu'à présent) lighbox div pour lui-même présentant un lien de phishing ou autre ...
tbc

7
Une autre idée serait de simplement effacer complètement la page avec quelque chose comme document.write("");(après avoir établi qu'elle est en train d'être encadrée
gabeio

211

6
excellent, soutenir cela dans le navigateur .exe est la voie à suivre sans aucun doute. Lorsque vous dites "la plupart des navigateurs", lesquels spécifiquement? Je ne trouve pas de bonnes sources pour autre chose que IE8.
Jeff Atwood

2
Voici une page de test: enhanie.com/test/clickjack . Prise en charge de Chrome 4.1.249.1042. Opéra 10.50 prend en charge. Firefox 3.6.2 ne prend pas encore en charge. Prise en charge de Safari 4.0.3.
EricLaw

1
Firefox 3.6.9 le prendra en charge de manière native ( hackademix.net/2010/08/31/… ) et toute installation de Firefox avec NoScript l'a depuis le début de 2009 ( hackademix.net/2009/01/29/x-frame- options-in-firefox )
ssokolow

4
Il est préférable de combiner cela avec le framebuster javascript.
Jesse Weigert

1
Ou combinez cela avec la réponse de décembre '12 sur cette page: stackoverflow.com/a/13708510/328397
goodguys_activate

34

Nous avons utilisé l'approche suivante dans l'un de nos sites Web à partir de 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! Je ne sais pas pourquoi cela n'est pas plus voté car c'est la meilleure réponse (à côté de la réponse X-Frame-Options, mais il est préférable de combiner les deux)
Jesse Weigert

1
Cette réponse ou la mienne doit être sélectionnée :)

J'aime votre idée d'utiliser à la fois cela et les options X-Frame: refuser la réponse.
Max West

Cela nécessite JS, qui est un assez gros fardeau à mon humble avis.
Navin

1
+1 - C'est la meilleure réponse lorsque X-FRAME-OPTIONS ne peut pas être utilisé. (Par exemple, lorsque vous devez autoriser ou refuser conditionnellement selon le référent.)
Jay Sullivan

29

Entré avec cela, et cela semble fonctionner au moins dans Firefox et le navigateur Opera.

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

2
la solution de Jani et Jeff (une fois éditée) est correcte et fonctionne de manière équivalente; donner à Jani l'acceptation parce que sa solution a bien fonctionné sans aucune modification
Jeff Atwood

29
Cela ne fonctionnera que si les deux fenêtres sont du même domaine; un événement rare lorsque vous souhaitez échapper à un cadre.
James

S'il y a des cadres imbriqués impliqués, vous devrez parcourir la chaîne de cadres et supprimer tous les onbeforeunloadgestionnaires, pas seulement celui du dessus!
Christoph

12
clarification importante: cela a fonctionné pour moi car l'iframe src = était défini de manière dynamique, et donc la politique inter-domaines n'était PAS en vigueur. JP a tout à fait raison, dans un src statique = cela ne fonctionnerait pas.
Jeff Atwood

5
ok maintenant quelqu'un peut-il trouver un buster buster buster buster buster?
Epaga

23

Compte tenu de la norme HTML5 actuelle qui a introduit le bac à sable pour iframe, tous les codes de contournement de cadre fournis dans cette page peuvent être désactivés lorsque l'attaquant utilise le bac à sable car cela empêche l'iframe de suivre:

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

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

Maintenant, considérez que l'attaquant a utilisé le code suivant pour héberger votre site dans iframe:

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

Ensuite, tout le code de contournement des cadres JavaScript échouera.

Après avoir vérifié tous les codes de bus de trame, seule cette défense fonctionne dans tous les cas:

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

celle initialement proposée par Gustav Rydstedt, Elie Bursztein, Dan Boneh et Collin Jackson (2010)


19

Après avoir réfléchi pendant un petit moment, je pense que cela leur montrera qui est le patron ...

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

L'utilisation _topcomme paramètre cible pour window.open()le lancera dans la même fenêtre.


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

6

Je vais être courageux et jeter mon chapeau dans le ring sur celui-ci (aussi ancien soit-il), voir combien de votes négatifs je peux collecter.

Voici ma tentative, qui semble fonctionner partout où je l'ai testé (Chrome20, IE8 et 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);
}());

J'ai placé ce code dans le <head>et l'ai appelé depuis la fin du <body>pour s'assurer que ma page est affichée avant qu'elle ne commence à se disputer avec le code malveillant, je ne sais pas si c'est la meilleure approche, YMMV.

Comment ça marche?

... Je vous entends demander - eh bien la réponse honnête est, je ne sais pas vraiment . Il a fallu beaucoup d'efforts pour le faire fonctionner partout où je testais, et l'effet exact qu'il a varie légèrement selon l'endroit où vous l'exécutez.

Voici la pensée derrière cela:

  • Définissez une fonction pour qu'elle s'exécute à l'intervalle le plus bas possible. Le concept de base derrière l'une des solutions réalistes que j'ai vues est de remplir le planificateur avec plus d'événements que le buster-buster de trame.
  • Chaque fois que la fonction se déclenche, essayez de changer l'emplacement du cadre supérieur. Exigence assez évidente.
  • Planifiez également une fonction à exécuter immédiatement, ce qui prendra beaucoup de temps (empêchant ainsi le buster-buster de trame d'interférer avec le changement d'emplacement). J'ai choisi un XMLHttpRequest synchrone parce que c'est le seul mécanisme auquel je pense qui ne nécessite pas (ou du moins demande) l'interaction de l'utilisateur et ne mâche pas le temps CPU de l'utilisateur.

Pour mon http://mysite.tld/page-that-takes-a-while-to-load(la cible du XHR), j'ai utilisé un script PHP qui ressemble à ceci:

<?php sleep(5);

Ce qui se produit?

  • Chrome et Firefox attendent les 5 secondes pendant que le XHR se termine, puis redirigent avec succès vers l'URL de la page encadrée.
  • IE redirige presque immédiatement

Ne pouvez-vous pas éviter le temps d'attente dans Chrome et Firefox?

Apparemment non. Au début, j'ai pointé le XHR vers une URL qui retournerait un 404 - cela ne fonctionnait pas dans Firefox. Ensuite, j'ai essayé l' sleep(5);approche sur laquelle j'ai finalement atterri pour cette réponse, puis j'ai commencé à jouer avec la durée du sommeil de différentes manières. Je n'ai pas pu trouver de véritable modèle de comportement, mais j'ai trouvé que s'il était trop court, spécifiquement Firefox ne jouerait pas la balle (Chrome et IE semblent se comporter assez bien). Je ne sais pas quelle est la définition de "trop ​​court" en termes réels, mais 5 secondes semblent fonctionner à chaque fois.


Si des ninjas Javascript qui passent veulent expliquer un peu mieux ce qui se passe, pourquoi c'est (probablement) faux, peu fiable, le pire code qu'ils aient jamais vu, etc.


Il semble que vous pouvez supprimer toutes vos phrases inquiétantes
mplungjan

6

À partir de 2015, vous devez utiliser la frame-ancestorsdirective CSP2 pour cela. Ceci est implémenté via un en-tête de réponse HTTP.

par exemple

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

Bien sûr, peu de navigateurs prennent en charge CSP2, il est donc judicieux d'inclure l'ancien en- X-Frame-Optionstête:

X-Frame-Options: DENY

Je conseillerais d'inclure les deux de toute façon, sinon votre site continuerait d'être vulnérable aux attaques de détournement de clics dans les anciens navigateurs, et bien sûr, vous obtiendriez un cadrage indésirable même sans intention malveillante. La plupart des navigateurs se mettent à jour automatiquement ces jours-ci, mais vous avez toujours tendance à bloquer les utilisateurs d'entreprise sur les anciennes versions d'Internet Explorer pour des raisons de compatibilité des applications héritées.


1
Tous les principaux navigateurs prennent désormais en charge CSP. C'est la bonne réponse en 2019 et dans un avenir prévisible.
Stephen R

5

Ok, donc nous savons que c'était dans un cadre. Nous avons donc location.href vers une autre page spéciale avec le chemin comme variable GET. Nous expliquons maintenant à l'utilisateur ce qui se passe et fournissons un lien avec une option target = "_ TOP". C'est simple et fonctionnerait probablement (je ne l'ai pas testé), mais cela nécessite une certaine interaction de l'utilisateur. Peut-être pourriez-vous signaler le site incriminé à l'utilisateur et faire une honte de cliquetis à votre site quelque part .. Juste une idée, mais ça marche la nuit ..


5

Toutes les solutions proposées forcent directement un changement d'emplacement de la fenêtre supérieure. Et si un utilisateur souhaite que le cadre soit là? Par exemple, le cadre supérieur dans les résultats d'image des moteurs de recherche.

J'ai écrit un prototype où par défaut toutes les entrées (liens, formulaires et éléments d'entrée) sont désactivées et / ou ne font rien lorsqu'elles sont activées.

Si un cadre contenant est détecté, les entrées restent désactivées et un message d'avertissement s'affiche en haut de la page. Le message d'avertissement contient un lien qui ouvrira une version sûre de la page dans une nouvelle fenêtre. Cela empêche la page d'être utilisée pour le détournement de clics, tout en permettant à l'utilisateur d'afficher le contenu dans d'autres situations.

Si aucune trame contenant n'est détectée, les entrées sont activées.

Voici le code. Vous devez définir les attributs HTML standard sur des valeurs sûres et ajouter des attributs supplémentaires qui contiennent les valeurs réelles. Il est probablement incomplet et pour une sécurité totale, des attributs supplémentaires (je pense aux gestionnaires d'événements) devront probablement être traités de la même manière:

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

Le problème est que le fabricant de cadres pourrait utiliser la position: absolu pour placer le bouton actif au-dessus de vos boutons inactifs et l'utilisateur ne verra que votre page Web et pensera qu'il clique sur VOS boutons.
jmucchiello

Le message d'avertissement serait toujours affiché, mais bien sûr, il est facile de couvrir le lien vers la page sécurisée comme vous le suggérez. Mais pourquoi se donner la peine de cadrer ma page pour amener les gens à cliquer sur un bouton familier si vous pouvez simplement copier la page et obtenir le même effet? Le code ci-dessus empêche principalement le détournement de clics. Si vous affichez ma page de manière invisible au-dessus d'une autre page, il n'est pas possible d'appeler des actions sur mon site.
Johan Stuyts

Si cela est placé dans un cadre de zone restreinte IE8 ou un cadre de bac à sable Chrome, Javascript ne s'exécutera jamais. Je me demande quelles modifications sont nécessaires dans ces cas
goodguys_activate

4

Eh bien, vous pouvez modifier la valeur du compteur, mais c'est évidemment une solution fragile. Vous pouvez charger votre contenu via AJAX après avoir déterminé que le site n'est pas dans un cadre - ce n'est pas non plus une excellente solution, mais cela évite, espérons-le, de déclencher l'événement on beforeunload (je suppose).

Edit: Une autre idée. Si vous détectez que vous êtes dans un cadre, demandez à l'utilisateur de désactiver javascript, avant de cliquer sur un lien qui vous mène à l'URL souhaitée (en passant une chaîne de requête qui informe votre page pour dire à l'utilisateur qu'il peut réactiver javascript une fois qu'il a sont là).

Edit 2: Go Nuclear - si vous détectez que vous êtes dans un cadre, supprimez simplement le contenu du corps de votre document et imprimez un message désagréable.

Edit 3: pouvez-vous énumérer le document supérieur et définir toutes les fonctions sur null (même les anonymes)?


Outlook (anciennement Hotmail) «devient nucléaire» s'il ne peut pas sortir d'un cadre - il place le contenu entier de l' <body>intérieur d'une <plaintext>balise sur display: none. C'est assez efficace.
uınbɐɥs

4

Si vous ajoutez une alerte juste après le code buster, l'alerte bloquera le fil javascript et laissera la page se charger. C'est ce que fait StackOverflow, et il sort de mes iframes, même lorsque j'utilise le buster de suppression de trame. Cela a également fonctionné avec ma page de test simple. Cela n'a été testé que dans Firefox 3.5 et IE7 sur Windows.

Code:

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

3

Je pense que tu y étais presque. As-tu essayé:

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

Ou bien:

window.parent.prevent_bust = 0;

Remarque: je n'ai pas réellement testé cela.


1
J'ai édité votre exemple de code (le test du parent semble échouer) mais la version éditée semble fonctionner!
Jeff Atwood

1
Cool. Il est toujours difficile de répondre avec du code non testé - je le fais pour au moins faire passer l'idée - et de laisser le pauvre demandeur déboguer. :)
Jeff Meatball Yang

12
Ne fonctionnera pas si le parent est sur un domaine différent, ce qui est probablement le cas!
Josh Stodola

2

Que diriez-vous d'appeler le buster à plusieurs reprises également? Cela créera une condition de course, mais on peut espérer que le buster arrive en tête:

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

2

Si vous regardez les valeurs renvoyées par setInterval()ce sont généralement des chiffres simples, vous pouvez donc généralement désactiver toutes ces interruptions avec une seule ligne de code:

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


0

setInterval et setTimeout créent un intervalle d'incrémentation automatique. Chaque fois que setTimeout ou setInterval est appelé, ce nombre augmente d'un, de sorte que si vous appelez setTimeout, vous obtiendrez la valeur actuelle la plus élevée.

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

Puisqu'il est presque inconnu qu'il y ait 10000 setIntervals et setTimeouts fonctionnant simultanément, et puisque setTimeout retourne "le dernier intervalle ou le timeout créé + 1", et puisque top.clearInterval est toujours accessible, cela vaincra les attaques de black-hat sur le frame les sites Web décrits ci-dessus.


0

Utilisez htaccess pour éviter le jeu de cadres à forte prise, l'iframe et tout contenu comme les images.

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

Cela affichera une page de copyright au lieu de celle attendue.


Cela repose sur le référent qui est a) pas toujours défini (en raison des paramètres du navigateur ou des extensions ou simplement parce que la page de référence utilise HTTPS sans utiliser <meta name="referrer" …/>et b) également défini lorsque vous cliquez sur des liens, de sorte que vous interdisez également les liens vers votre page et sautez la toile.
Martin
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.