web-dev-qa-db-fra.com

Frame Buster Buster ... code 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 du code JavaScript anti-cadrage dans toutes vos pages:

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

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

Comme il se trouve, votre code qui casse l'image peut être décomposé , comme montré 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 a les effets suivants:

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

Ma question est la suivante - et il s’agit plus d’un casse-tête javascript que d’un problème réel - comment vaincre le briseur de frame-busting?

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

  • tenter d'effacer l'événement onbeforeunload via onbeforeunload = null n'a eu aucun effet
  • l'ajout d'un alert() a arrêté le processus et a permis à l'utilisateur de savoir qu'il se passait, mais n'a pas interféré avec le code de quelque manière que ce soit; cliquer sur OK permet de continuer comme d'habitude
  • Je ne vois aucun moyen d'effacer le temporisateur setInterval()

Je ne suis pas un grand programmeur JavaScript, alors voici le défi que je vous lance: hé, êtes-vous capable de casser le buster qui casse les images?

408
Jeff Atwood

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 "page d'accueil", créez une méthode setInterval qui tente de rompre le cadre. Si après 3 ou 4 tentatives, 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 visualisez cette page dans une fenêtre de cadre non autorisée Blah blah ... 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 utiliser un scénario pour s'en sortir.

146
Hugoware
205
EricLaw

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>
33
Dungeon Hunter

Je suis arrivé à cela, et cela semble fonctionner au moins dans Firefox et dans le navigateur Opera.

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

En prenant en compte le standard HTML5 actuel qui a introduit le sandbox pour iframe, tous les codes de contournement d'images fournis dans cette page peuvent être désactivés lorsque l'attaquant utilise le sandbox, car il empêche l'iframe de ce qui suit:

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érons 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 d'images JavaScript échouera.

Après vérification de 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 proposée à l'origine par Gustav Rydstedt, Elie Bursztein, Dan Boneh et Collin Jackson (2010)

23
user1646111

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

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

Utiliser _top comme paramètre cible pour window.open() le lancera dans la même fenêtre.

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

Ok, alors nous savons que c'était dans un cadre. Nous avons donc location.href sur une autre page spéciale avec le chemin d'accès 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 interaction de l'utilisateur. Peut-être pourriez-vous indiquer le site incriminé à l'utilisateur et créer un hall de honte pour les jack jacks sur votre site quelque part .. Juste une idée, mais ça marche de nuit ..

5
Mike

Je vais être courageux et jeter mon chapeau dans l'anneau de celui-ci (ancien comme il est), voir combien de votes négatifs je peux collecter.

Voici ma tentative qui semble fonctionner partout où je l'ai testée (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 je l'ai appelé depuis la fin du <body> pour m'assurer que ma page est rendue avant qu'elle ne commence à discuter avec le code malveillant. Je ne sais pas si c'est la meilleure approche. YMMV.

Comment ça marche?

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

Voici la pensée derrière cela:

  • Définir une fonction pour s'exécuter à l'intervalle le plus bas possible. Le concept de base derrière toutes les solutions réalistes que j'ai vues est de remplir le planificateur avec plus d'événements que le frame buster-buster.
  • Chaque fois que la fonction est activée, essayez de modifier l'emplacement du cadre supérieur. Exigence assez évidente.
  • Planifiez également l'exécution immédiate d'une fonction, ce qui prendra beaucoup de temps (empêchant ainsi le bus de trames d'interférer avec le changement d'emplacement). J'ai choisi une requête XMLHttpRequest synchrone, car c'est le seul mécanisme auquel je puisse penser qui ne nécessite pas (ou au moins demande) une interaction 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 ressemblant à ceci:

<?php sleep(5);

Qu'est-ce qui se passe?

  • Chrome et Firefox attendent les 5 secondes pendant la fin de la XHR, puis redirigent avec succès vers l'URL de la page encadrée.
  • IE redirige quasiment 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 renverrait un 404 - cela ne fonctionnait pas dans Firefox. Ensuite, j'ai essayé l'approche sleep(5); que j'ai finalement retenue pour cette réponse, puis j'ai commencé à jouer avec la durée du sommeil de différentes manières. Je ne pouvais trouver aucun comportement réel dans le comportement, mais j’ai trouvé que s’il était trop court, plus précisément Firefox ne jouerait pas à la balle (Chrome et IE semblaient s’être assez bien comportés). Je ne sais pas ce que la définition de "trop ​​court" est en termes réels, mais 5 secondes semblent fonctionner à chaque fois.


Si des ninjas javascript 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., que j'écouterai avec plaisir.

5
DaveRandom

Toutes les solutions proposées imposent directement un changement d’emplacement de la fenêtre supérieure. Et si un utilisateur veut 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 sont laissées 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 l'utilisation de la page pour le détournement de clic, tout en permettant à l'utilisateur de visualiser le contenu dans d'autres situations.

Si aucun cadre contenant n'est détecté, 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 contenant les valeurs réelles. Il est probablement incomplet et pour des raisons de sécurité, 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>
5
Johan Stuyts

À partir de 2015, vous devriez utiliser la directive frame-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, beaucoup de navigateurs ne supportent pas encore CSP2, il est donc sage d'inclure l'ancien X-Frame-Options en-tête:

_X-Frame-Options: DENY
_

Je conseillerais d'inclure les deux de toute façon, sinon votre site continuerait à être vulnérable aux attaques Clickjacking 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 de nos jours, mais vous avez toujours tendance à contraindre les utilisateurs d'entreprise à utiliser les anciennes versions d'Internet Explorer pour des raisons de compatibilité des applications héritées.

5
SilverlightFox

Si vous ajoutez une alerte juste après le code buster, celle-ci 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 bus de contournement des images. Cela a également fonctionné avec ma page de test simple. Cela n'a été testé que dans Firefox 3.5 et IE7 sous Windows.

Code:

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

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 - ce n’est pas non plus une bonne solution, mais cela évite, espérons-le, de déclencher l’événement 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 menant à l'URL souhaitée (en passant une chaîne de requête qui permet à votre page de dire à l'utilisateur qu'il peut réactiver le javascript une fois 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 mauvais message.

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

4
RedFilter

Je pense que tu étais presque là. 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.

3

Si vous examinez les valeurs renvoyées par setInterval(), elles sont généralement composées d'un seul chiffre. 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)
2
Robin Nixon

Qu'en est-il d'appeler le buster à plusieurs reprises aussi? Cela va créer une situation de concurrence critique, mais on peut espérer que le vainqueur l'emporte:

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

Je viens peut-être de trouver un moyen de casser le javascript du buster buster. En utilisant getElementsByName dans ma fonction javascript, j'ai défini une boucle entre le bus de trame et le script de bus de trame réel. vérifier ce post. http://www.phcityonweb.com/frame-buster-buster-buster-2426

2
Phcityonweb

Utilisez htaccess pour éviter les jeux de cadres très volumineux, les iframe et tout contenu tel que des images.

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

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

0
B.F.

setInterval et setTimeout créent un intervalle d'incrémentation automatique. Chaque fois que setTimeout ou setInterval est appelé, ce nombre augmente d'un unité. Ainsi, si vous appelez setTimeout, vous obtenez 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";
   }

Etant donné qu'il est pratiquement impossible d'entendre 10000 setIntervals et setTimeouts simultanés fonctionner, et puisque setTimeout renvoie "le dernier intervalle ou le délai d'expiration créé + 1" et que top.clearInterval est toujours accessible, les attaques de type chapeau noir seront supprimées. les sites Web décrits ci-dessus.

0
cwallenpoole