Frame Buster Buster … code de buster nécessaire

Disons que vous ne voulez pas que d'autres sites "encadrent" votre site dans un <iframe> :

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

Donc, vous insérez un code anti-encadrement, la suppression de l'image dans toutes vos pages:

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

Excellent! Maintenant, vous «bustez» ou sortez de tout iframe contenant automatiquement. Sauf pour un petit problème.

Il s'avère que votre code de blocage peut être interrompu , comme illustré 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 fait ce qui suit:

  • Augmente un compteur chaque fois que le navigateur tente de naviguer loin de la page en cours, via le gestionnaire d'événements window.onbeforeunload
  • setInterval() une minuterie qui déclenche chaque milliseconde via setInterval() , et si elle voit le compteur augmenté, modifie l'emplacement actuel sur un serveur du contrôle de l'attaquant
  • Ce serveur sert une page avec le code d'état HTTP 204 , qui ne provoque pas que le navigateur navigue partout

Ma question est – et c'est plus un casse-tête JavaScript qu'un problème réel – comment pouvez-vous vaincre le buster de trame?

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

  • onbeforeunload d'effacer l'événement onbeforeunload = null via onbeforeunload = null n'a eu aucun effet
  • L'ajout d'une alert() arrêté le processus pour permettre à l'utilisateur de savoir qu'il se passait, mais n'a pas entravé le code; En cliquant sur OK, le busting continue comme d'habitude
  • Je ne peux penser à aucun moyen d'effacer la setInterval()

Je ne suis pas un programmeur de JavaScript, alors voici mon défi pour vous: hey buster, pouvez-vous faire tomber le buster à l'arrière-plan?

Je ne sais pas si cela est viable ou non, mais si vous ne pouvez pas casser le cadre, pourquoi ne pas afficher un avertissement. Par exemple, si votre page n'est pas la "page supérieure", créez une méthode setInterval qui essaie de briser le cadre. Si après 3 ou 4 essais, votre page n'est toujours pas la page supérieure – 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 – (Blah blah … problème de sécurité potentiel)

Cliquez sur ce lien pour corriger ce problème

Pas le meilleur, mais je ne vois pas comment ils pouvaient faire leur chemin.

Je suis passé avec ça, et cela semble fonctionner au moins dans Firefox.

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

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> 

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

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

En utilisant _top comme paramètre cible pour window.open() le lancer dans la même fenêtre.

Compte tenu de la norme HTML5 actuelle qui a introduit sandbox for iframe, tous les codes de suppression de trame fournis dans cette page peuvent être désactivés lorsque l'attaquant utilise un sandbox car il restreint l'iframe de suite:

 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 

Voir: 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 code de suppression de cadre 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> 

Qui a été initialement proposé par Gustav Rydstedt, Elie Bursztein, Dan Boneh et Collin Jackson (2010)

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

D'accord, alors nous savons qu'ils étaient dans un cadre. Nous allons donc localiser.href à une autre page spéciale avec le chemin en tant que 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 (ne l'a pas testé), mais cela nécessite une interaction avec les utilisateurs. Peut-être que vous pourriez signaler le site offensant à l'utilisateur et faire une salle de la honte de Click Jackers à votre site quelque part … Juste une idée, mais la nuit travaille …

Toutes les solutions proposées forcent directement une modification de l'emplacement de la fenêtre supérieure. Que faire si un utilisateur veut que le cadre soit là? Par exemple, le cadre supérieur de l'image résulte 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 une trame contenant est détectée, les entrées sont 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écurisée de la page dans une nouvelle fenêtre. Cela empêche que la page ne soit utilisée pour cliqueter, tout en permettant à l'utilisateur de voir le contenu dans d'autres situations.

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

Voici le code. Vous devez définir les attributs HTML standard à des valeurs sûres et ajouter des attributs supplémentaires contenant les valeurs réelles. Il est probablement incomplet et pour la sécurité complète 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> 

Je serai brave et je lancerai mon chapeau dans l'anneau sur celui-ci (ancien comme il est), voyez combien de contraintes 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 la <head> et l'ai appelé à partir de la fin du <body> pour s'assurer que ma page est rendue avant qu'elle ne commence à discuter avec le code malveillant, ne sais pas si c'est la meilleure approche, YMMV.

Comment ça marche?

… Je vous entends demander … bien, la réponse honnête est que je ne sais pas vraiment . Il a fallu beaucoup de fudging 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 elle:

  • Définissez une fonction à exécuter à l'intervalle le plus bas possible. Le concept de base de l'une des solutions réalistes que j'ai vus 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 modifier l'emplacement du cadre supérieur. Exigence tout à fait évidente.
  • Égalisez également une fonction à exécuter immédiatement qui prendra beaucoup de temps à compléter (bloquant ainsi le buster-buster de trame pour interférer avec le changement de localisation). J'ai choisi un XMLHttpRequest synchrone parce que c'est le seul mécanisme dont je peux penser que cela 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 est terminé, puis redirigez avec succès vers l'URL de la page encadrée.
  • IE redirige à peu près immédiatement

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

Apparemment non. Au début, j'ai souligné le XHR à une URL qui renverrait un 404 – cela ne fonctionnait pas dans Firefox. Puis j'ai essayé le sleep(5); Approche que j'ai finalement atterri pour cette réponse, alors j'ai commencé à jouer avec la longueur de sommeil de diverses façons. Je ne trouvais aucun motif réel pour le comportement, mais je l'ai trouvé si c'est trop court, en particulier Firefox ne jouera pas à la balle (Chrome et IE semblent assez bien portés). Je ne sais pas quelle est la définition de «trop court» en termes réels, mais 5 secondes semblent fonctionner à chaque fois.


Si les ninjas Javascript autorisés veulent expliquer un peu mieux ce qui se passe, pourquoi il est (probablement) mauvais, peu fiable, le pire code qu'ils ont jamais vu, etc. J'écouterai avec plaisir.

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 ne se trouve pas dans un cadre – pas aussi une excellente solution, mais il espère éviter 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 le javascript avant de cliquer sur un lien qui vous amène à l'URL souhaitée (en passant une requête qui permet à votre page de dire à l'utilisateur qu'ils peuvent réactiver le javascript une fois qu'ils ont sont là).

Edit 2: Go nuclear – si vous détectez que vous êtes dans un cadre, supprimez simplement le contenu de votre corps de document et imprimez un message méchant.

Edit 3: pouvez-vous énumérer le document principal et définir toutes les fonctions à nulle (même anonymes)?

À partir de 2015, vous devez utiliser la directive frame-ancestors ancestors de 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, il n'y a pas beaucoup de navigateurs compatibles avec CSP2, il est donc judicieux d'inclure l'ancien en X-Frame-Options tête X-Frame-Options :

 X-Frame-Options: DENY 

Je vous conseille d'inclure les deux de toute façon, sinon votre site continuerait à être vulnérable aux attaques de Clickjacking dans les anciens navigateurs, et bien sûr, vous obtiendrez des cadrages indésirables même sans intention malveillante. La plupart des navigateurs ne se mettent à jour automatiquement ces jours-ci, mais vous avez toujours tendance à empêcher les utilisateurs d'être bloqués sur les anciennes versions d'Internet Explorer pour des raisons de compatibilité des applications existantes.

Je pense que vous étiez presque là. As-tu essayé:

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

Ou, en variante:

 window.parent.prevent_bust = 0; 

Remarque: je n'ai pas vraiment testé cela.

Si vous ajoutez une alerte directement après le code de buster, l'alerte bloquera le thread de JavaScript, et cela laissera le chargement de la page. C'est ce qu'est StackOverflow, et il sort de mes iframes, même si j'utilise le buster. Il a également fonctionné avec ma page de test simple. Cela n'a été testé que sur 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> 

Qu'en est-il d'appeler le buster à plusieurs reprises aussi? Cela créera une condition de course, mais on peut espérer que le buster sort sur le dessus:

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

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

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

Je pourrais simplement avoir un moyen de boucher le jeter image buster buster. À l'aide de getElementsByName dans ma fonction javascript, j'ai configuré une boucle entre le buster frame et le script buster buster réel. Vérifiez cette publication. http://www.phcityonweb.com/frame-buster-buster-buster-2426

SetInterval et setTimeout créent un intervalle d'incrémentation automatique. Chaque fois que setTimeout ou setInterval est appelé, ce nombre augmente d'un seul, de sorte que si vous appelez setTimeout, vous obtiendrez la valeur actuelle et 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"; } 

Étant donné qu'il est presque inouï pour qu'il y ait 10000 configurations simultanées et SetTimeouts fonctionnant, et puisque setTimeout retourne "dernier intervalle ou timeout créé + 1", et puisque top.clearInterval est toujours accessible, cela va vaincre les attaques black-hat au cadre Les sites Web décrits ci-dessus.

Utilisez htaccess pour éviter le jeu de cadres, 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 des attentes.

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