SecurityError: Blockiert einen Frame mit Ursprung für den Zugriff auf einen rahmenübergreifenden Frame

555

Ich lade eine <iframe>in meine HTML-Seite und versuche, mit Javascript auf die darin enthaltenen Elemente zuzugreifen. Wenn ich jedoch versuche, meinen Code auszuführen, wird folgende Fehlermeldung angezeigt:

SecurityError: Blocked a frame with origin "http://www.<domain>.com" from accessing a cross-origin frame.

Können Sie mir bitte helfen, eine Lösung zu finden, damit ich auf die Elemente im Rahmen zugreifen kann?

Ich benutze diesen Code zum Testen, aber vergebens:

$(document).ready(function() {
    var iframeWindow = document.getElementById("my-iframe-id").contentWindow;

    iframeWindow.addEventListener("load", function() {
        var doc = iframe.contentDocument || iframe.contentWindow.document;
        var target = doc.getElementById("my-target-id");

        target.innerHTML = "Found it!";
    });
});
Mubashermubi
quelle

Antworten:

820

Politik gleicher Herkunft

Sie können<iframe> mit JavaScript nicht auf eine andere Herkunft zugreifen. Dies wäre eine große Sicherheitslücke, wenn Sie dies tun könnten. Für Richtlinienbrowser mit demselben Ursprung blockieren Skripte, die versuchen, auf einen Frame mit einem anderen Ursprung zuzugreifen .

Die Herkunft wird als unterschiedlich angesehen, wenn mindestens einer der folgenden Teile der Adresse nicht beibehalten wird:

<protocol>://<hostname>:<port>/...

Protokoll , Hostname und Port müssen mit Ihrer Domain identisch sein, wenn Sie auf einen Frame zugreifen möchten.

Hinweis: Internet Explorer nicht streng an diese Regel bekannt ist, finden Sie hier für weitere Details.

Beispiele

Folgendes würde passieren, wenn Sie versuchen, auf die folgenden URLs zuzugreifen http://www.example.com/home/index.html

URL                                             RESULT 
http://www.example.com/home/other.html       -> Success 
http://www.example.com/dir/inner/another.php -> Success 
http://www.example.com:80                    -> Success (default port for HTTP) 
http://www.example.com:2251                  -> Failure: different port 
http://data.example.com/dir/other.html       -> Failure: different hostname 
https://www.example.com/home/index.html:80   -> Failure: different protocol
ftp://www.example.com:21                     -> Failure: different protocol & port 
https://google.com/search?q=james+bond       -> Failure: different protocol, port & hostname 

Problemumgehung

Auch wenn Same Origin Policy Blöcke Skripte von den Inhalt der Seiten mit einer anderen Herkunft zugreifen, wenn Sie beide Seiten besitzen, können Sie dieses Problem mit umgehen window.postMessageund seine relative messageEreignismeldungen zwischen den beiden Seiten zu senden, wie folgt aus :

  • Auf Ihrer Hauptseite:

    let frame = document.getElementById('your-frame-id');
    frame.contentWindow.postMessage(/*any variable or object here*/, 'http://your-second-site.com');

    Das zweite Argument postMessage()kann darin bestehen '*', keine Präferenz für den Ursprung des Ziels anzugeben. Wenn möglich, sollte immer ein Zielursprung angegeben werden, um zu vermeiden, dass die Daten, die Sie an eine andere Site senden, weitergegeben werden.

  • In Ihrem <iframe>(auf der Hauptseite enthalten):

    window.addEventListener('message', event => {
        // IMPORTANT: check the origin of the data! 
        if (event.origin.startsWith('http://your-first-site.com')) { 
            // The data was sent from your site.
            // Data sent with postMessage is stored in event.data:
            console.log(event.data); 
        } else {
            // The data was NOT sent from your site! 
            // Be careful! Do not use it. This else branch is
            // here just for clarity, you usually shouldn't need it.
            return; 
        } 
    }); 

Diese Methode kann in beide Richtungen angewendet werden , indem auch auf der Hauptseite ein Listener erstellt und Antworten vom Frame empfangen werden. Dieselbe Logik kann auch in Popups und grundsätzlich in jedem neuen Fenster implementiert werden, das von der Hauptseite generiert wird (z. B. mithilfe von window.open()), ohne Unterschied.

Deaktivieren der Richtlinie für denselben Ursprung in Ihrem Browser

Es gibt bereits einige gute Antworten zu diesem Thema (ich habe sie gerade beim Googeln gefunden). Für die Browser, in denen dies möglich ist, werde ich die entsprechende Antwort verknüpfen. Beachten Sie jedoch, dass das Deaktivieren der Richtlinie mit demselben Ursprung nur Ihren Browser betrifft . Wenn Sie einen Browser mit deaktivierten Sicherheitseinstellungen für denselben Ursprung ausführen, erhält jede Website Zugriff auf Ressourcen mit unterschiedlichen Ursprüngen. Dies ist daher sehr unsicher und sollte NIEMALS durchgeführt werden, wenn Sie nicht genau wissen, was Sie tun (z . B. Entwicklungszwecke) .

Marco Bonelli
quelle
27
Jede andere Antwort, die ich 1 , 2 gefunden habe , legt nahe, dass CORS / Access-Control-Allow-Originnicht für iFrames gilt, sonderncanvas.drawImage nur für XHRs, Schriftarten, WebGL und . Ich glaube, das postMessageist die einzige Option.
SnappieT
369
Zum ersten Mal habe ich den Tilde-Operator "~" in Javascript gesehen. Für alle anderen, die auch nicht wussten, was es tut: Es konvertiert -1 in 0, was erspart, dass Sie "! = -1" für das Ergebnis des indexOf tun müssen. Persönlich denke ich, dass ich weiterhin "! = -1" verwenden werde, da es für andere Programmierer einfacher ist, die Fehler zu verstehen und zu vermeiden, die entstehen würden, wenn man vergisst, die Tilde einzulegen. (Aber es ist immer schön, etwas Neues zu lernen.)
Redzarf
4
@SabaAhang suchen Sie einfach nach dem iframe.src, und wenn sich die Site vom Hostnamen Ihrer Domain unterscheidet, können Sie nicht auf diesen Frame zugreifen.
Marco Bonelli
17
@Snuggs völlig falsch, ~gibt das 2er-Komplement der Zahl zurück, nwird also -n-1, was bedeutet, dass nur -1wird 0(was als interpretiert wird false) und jeder andere Wert den Test besteht. IE 0 = -(-1)-1nicht -(-1+1).
Marco Bonelli
2
@ user2568374 location.ancestorOrigins[0]ist der Speicherort des übergeordneten Frames. Wenn Ihr Frame auf einer anderen Site ausgeführt wird und Sie überprüfen event.origin.indexOf(location.ancestorOrigins[0]), ob der Ursprung des Ereignisses die Frame-Adresse des übergeordneten Elements enthält, die immer sein wirdtrue , erlauben Sie jedem übergeordneten Frame mit einem beliebigen Ursprung , auf Ihren Frame zuzugreifen ist offensichtlich nicht etwas, was Sie tun möchten. Darüber hinaus document.referrerist auch schlechte Praxis, wie ich bereits in den Kommentaren oben erklärt habe.
Marco Bonelli
55

Ergänzung zu Marco Bonellis Antwort: Die derzeit beste Art der Interaktion zwischen Frames / Iframes ist die Verwendung window.postMessage, die von allen Browsern unterstützt wird

Geert
quelle
21
Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert. - Aus der Rezension
Alessandro Cuttin
9
Ich bin anderer Meinung, @AlessandroCuttin. Zu erklären, wie es window.postMessagefunktioniert, würde nur die akzeptierte Antwort duplizieren, auf die ich bereits verweise. Darüber hinaus ist der wesentliche Wert, den meine Antwort hinzufügt, genau der, auf externe Dokumentation zu verweisen.
Geert
5
Ich denke, es ist besser, wenn Sie die akzeptierte Antwort bearbeiten und dort hinzufügen können
Martin Massera
12
window.postMessage können wir nur verwenden, wenn wir sowohl auf das übergeordnete (unsere HTML-Seite) als auch auf das untergeordnete Element (anderer Domain-Iframe) zugreifen können. Andernfalls wird "ES GIBT KEINE MÖGLICHKEIT" immer der Fehler "Nicht erfasste DOMException: Blockierter Frame" ausgegeben mit dem Ursprung "< yourdomainname.com >" vom Zugriff auf einen Cross-Origin-Frame. "
VIJAY P
19

Überprüfen Sie den Webserver der Domain auf http://www.<domain>.comKonfiguration. X-Frame-Options Es handelt sich um eine Sicherheitsfunktion, die ClickJacking-Angriffe verhindert.

Wie funktioniert clickJacking?

  1. Die böse Seite sieht genauso aus wie die Opferseite.
  2. Dann hat es Benutzer dazu gebracht, ihren Benutzernamen und ihr Passwort einzugeben.

Technisch hat das Böse eine iframemit der Quelle zur Opferseite.

<html>
    <iframe src='victim_domain.com'/>
    <input id="username" type="text" style="display: none;/>
    <input id="password" type="text" style="display: none;/>
    <script>
        //some JS code that click jacking the user username and input from inside the iframe...
    <script/>
<html>

Wie die Sicherheitsfunktion funktioniert

Wenn Sie verhindern möchten, dass Webserveranforderungen innerhalb eines X gerendert werden, iframefügen Sie die X-Frame-Optionen hinzu

X-Frame-Optionen VERWEIGERN

Die Optionen sind:

  1. SAMEORIGIN // Nur meiner eigenen Domain erlauben, mein HTML in einem Iframe zu rendern.
  2. VERWEIGERN // erlaube nicht, dass mein HTML in einem Iframe gerendert wird
  3. "ALLOW-FROM https://example.com/ " // Erlaube einer bestimmten Domain, meinen HTML- Code in einem Iframe zu rendern

Dies ist ein IIS-Konfigurationsbeispiel:

   <httpProtocol>
       <customHeaders>
           <add name="X-Frame-Options" value="SAMEORIGIN" />
       </customHeaders>
   </httpProtocol>

Die Lösung der Frage

Wenn der Webserver die Sicherheitsfunktion aktiviert hat, kann dies zu einem clientseitigen Sicherheitsfehler führen.

Shahar Shokrani
quelle
1
Ich glaube nicht, dass X-Frame-Optionen hier gelten - X-Frame-Optionen, die von der (eingebetteten) Gastseite definiert werden, können dazu führen, dass das übergeordnete Element das Laden der Seite verweigert, aber soweit ich weiß, hat dies keinen Einfluss auf Javascript Zugriff - auch mit X-Frame-Optionen: *, ich glaube nicht, dass Sie mit Javascript auf das DOM einer anderen Ursprungsgastseite zugreifen können
Noah Gilmore
13

Für mich wollte ich einen 2-Wege-Handshake implementieren, was bedeutet:
- Das übergeordnete Fenster wird schneller geladen als der Iframe.
- Der Iframe sollte mit dem übergeordneten Fenster sprechen, sobald es fertig ist
- Das übergeordnete ist bereit, die Iframe-Nachricht zu empfangen und wiederzugeben

Dieser Code wird verwendet, um das weiße Etikett im Iframe mithilfe des Codes [CSS Custom Property]
:
Iframe festzulegen

$(function() {
    window.onload = function() {
        // create listener
        function receiveMessage(e) {
            document.documentElement.style.setProperty('--header_bg', e.data.wl.header_bg);
            document.documentElement.style.setProperty('--header_text', e.data.wl.header_text);
            document.documentElement.style.setProperty('--button_bg', e.data.wl.button_bg);
            //alert(e.data.data.header_bg);
        }
        window.addEventListener('message', receiveMessage);
        // call parent
        parent.postMessage("GetWhiteLabel","*");
    }
});

Elternteil

$(function() {
    // create listener
    var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
    var eventer = window[eventMethod];
    var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
    eventer(messageEvent, function (e) {
        // replay to child (iframe) 
        document.getElementById('wrapper-iframe').contentWindow.postMessage(
            {
                event_id: 'white_label_message',
                wl: {
                    header_bg: $('#Header').css('background-color'),
                    header_text: $('#Header .HoverMenu a').css('color'),
                    button_bg: $('#Header .HoverMenu a').css('background-color')
                }
            },
            '*'
        );
    }, false);
});

Natürlich können Sie die Herkunft und den Text einschränken. Dies ist ein einfach zu
handhabender Code. Ich fand dieses Beispiel hilfreich: [ Domainübergreifendes
Messaging mit postMessage]

Yakir Manor
quelle
Ich habe es mit einem Problem mit Safari zu tun, bei dem das Dokument im Iframe seine JS später als die übergeordnete Seite ausführt, wodurch die Nachricht früher gesendet wird als das Dokument im Iframe Nachrichten abhört. Was ist genau das Gegenteil von dem, was Chrome und Firefox tun? Haben Sie Ihren Code in einer Safari auf iOS getestet? Übrigens ist postMessage mit dem zweiten Parameter des Wertes "*" nicht ganz sicher, Sie sollten immer domain angeben
sKopheK
Ihr erster Codeblock befindet sich auf dem Iframe im übergeordneten Element oder auf der Seite, die in den Iframe geladen wird?
Demonic218
0

Ich möchte eine Java Spring-spezifische Konfiguration hinzufügen, die sich darauf auswirken kann.

In der Website oder Gateway-Anwendung gibt es eine contentSecurityPolicy-Einstellung

Im Frühjahr finden Sie die Implementierung der Unterklasse WebSecurityConfigurerAdapter

contentSecurityPolicy("
script-src 'self' [URLDomain]/scripts ; 
style-src 'self' [URLDomain]/styles;
frame-src 'self' [URLDomain]/frameUrl...

...

.referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)

Der Browser wird blockiert, wenn Sie hier keinen sicheren externen Inhalt definiert haben.

ssp
quelle
0

Wenn Sie die Kontrolle über den Inhalt des Iframes haben, dh wenn er lediglich in einem Cross-Origin-Setup wie Amazon Mechanical Turk geladen wird, können Sie dieses Problem mit dem umgehen <body onload='my_func(my_arg)'> Attribut für das innere HTML .

Verwenden Sie beispielsweise für das innere HTML den thisParameter html (yes - thisist definiert und bezieht sich auf das übergeordnete Fenster des inneren Body-Elements):

<body onload='changeForm(this)'>

Im inneren HTML:

    function changeForm(window) {
        console.log('inner window loaded: do whatever you want with the inner html');
        window.document.getElementById('mturk_form').style.display = 'none';
    </script>
Zhanwen Chen
quelle
-24
  • Öffnen Sie das Startmenü
  • Geben Sie windows + R ein oder öffnen Sie "Ausführen"
  • Führen Sie den folgenden Befehl aus.

chrome.exe --user-data-dir="C://Chrome dev session" --disable-web-security

Sakthi Sudhan
quelle
3
Gut für schnelle und schmutzige Tests!
user1068352
6
Schrecklich für alles, was kein schneller und schmutziger Test ist… und bereits in der akzeptierten Antwort angesprochen.
Quentin
2
Selbst mit dem Befehl funktioniert es nicht, da Chrome die Deaktivierung der Websicherheit auf diese Weise vermeidet
Metafaniel