SecurityError: bloccato un frame con origine dall'accesso a un frame di origine incrociata


556

Sto caricando un <iframe>nella mia pagina HTML e provando ad accedere agli elementi al suo interno usando Javascript, ma quando provo ad eseguire il mio codice, ottengo il seguente errore:

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

Potete per favore aiutarmi a trovare una soluzione in modo da poter accedere agli elementi nella cornice?

Sto usando questo codice per i test, ma invano:

$(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!";
    });
});

Risposte:


821

Politica della stessa origine

Non puoi accedere a un <iframe>origine diversa utilizzando JavaScript, sarebbe un enorme difetto di sicurezza se potessi farlo. Per i criteri della stessa origine i browser bloccano gli script tentando di accedere a un frame con un'origine diversa .

L'origine è considerata diversa se almeno una delle seguenti parti dell'indirizzo non è mantenuta:

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

Il protocollo , il nome host e la porta devono essere gli stessi del tuo dominio, se vuoi accedere a un frame.

NOTA: Internet Explorer è noto per non seguire rigorosamente questa regola, vedere qui per i dettagli.

Esempi

Ecco cosa succederebbe provando ad accedere ai seguenti URL da 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 

Soluzione

Anche se la politica della stessa origine impedisce agli script di accedere al contenuto dei siti con un'origine diversa, se si possiede entrambe le pagine, è possibile aggirare questo problema utilizzando window.postMessagee il relativo messageevento relativo all'invio di messaggi tra le due pagine, in questo modo:

  • Nella tua pagina principale:

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

    Il secondo argomento postMessage()può essere '*'quello di non indicare alcuna preferenza sull'origine della destinazione. Se possibile, è necessario fornire sempre un'origine target, per evitare di divulgare i dati inviati a qualsiasi altro sito.

  • Nel tuo <iframe>(contenuto nella pagina principale):

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

Questo metodo può essere applicato in entrambe le direzioni , creando anche un listener nella pagina principale e ricevendo risposte dal frame. La stessa logica può essere implementata anche nei pop-up e praticamente in qualsiasi nuova finestra generata dalla pagina principale (ad es. Utilizzo window.open()), senza alcuna differenza.

Disattivazione criterio dell'origine nel tuo navigatore

Ci sono già alcune buone risposte su questo argomento (le ho appena trovate su Google), quindi, per i browser in cui ciò è possibile, collegherò la risposta relativa. Tuttavia, tieni presente che la disabilitazione della politica della stessa origine influirà solo sul tuo browser . Inoltre, l'esecuzione di un browser con le impostazioni di sicurezza della stessa origine disabilitate garantisce a qualsiasi sito Web l'accesso alle risorse di origine incrociata, quindi è molto pericoloso e non dovrebbe MAI essere fatto se non si sa esattamente cosa si sta facendo (ad es. Scopi di sviluppo) .


27
Qualsiasi altra risposta che ho trovato 1 , 2 , suggerisce che CORS / Access-Control-Allow-Originnon si applica a iFrame, ma solo a XHR, Fonts, WebGL ecanvas.drawImage . Credo postMessagesia l'unica opzione.
snappieT

370
La prima volta che ho visto l'operatore tilde "~" in javascript. Per chiunque non sappia anche cosa fa: converte da -1 a 0, il che evita di dover fare "! = -1" sul risultato di indexOf. Personalmente, penso che continuerò a usare "! = -1" poiché è più facile da capire per gli altri programmatori ed evita i bug che verrebbero dimenticati di inserire la tilde. (Ma è sempre bello imparare qualcosa di nuovo.)
Redzarf,

4
@SabaAhang controlla solo per iframe.src, e se il sito è diverso dal nome host del tuo dominio, non puoi accedere a quel frame.
Marco Bonelli,

18
@Snuggs è totalmente sbagliato, ~restituisce il complemento 2 del numero, quindi ndiventa -n-1, il che significa che solo -1diventerà 0(che viene interpretato come false) e qualsiasi altro valore supererà il test. IE 0 = -(-1)-1, no -(-1+1).
Marco Bonelli,

2
@ user2568374 location.ancestorOrigins[0]è la posizione del frame principale. Se il tuo frame è in esecuzione all'interno di un altro sito e controlli usando event.origin.indexOf(location.ancestorOrigins[0])stai controllando se l'origine dell'evento contiene l'indirizzo del frame del genitore, che sarà sempre cosìtrue , quindi stai permettendo a qualsiasi genitore con qualsiasi origine di accedere al tuo frame, e questo ovviamente non è qualcosa che vuoi fare. Inoltre, document.referrerè anche una cattiva pratica, come ho già spiegato nei commenti sopra.
Marco Bonelli,

55

Completando la risposta di Marco Bonelli: il miglior modo attuale di interagire tra frame / iframe è window.postMessage, supportato da tutti i browser


21
Sebbene questo collegamento possa rispondere alla domanda, è meglio includere qui le parti essenziali della risposta e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia. - Dalla recensione
Alessandro Cuttin

9
Non sono d'accordo, @AlessandroCuttin. Spiegare come window.postMessagefunziona sarebbe solo duplicare la risposta accettata a cui già faccio riferimento. Inoltre, il valore essenziale che la mia risposta aggiunge è esattamente quello del riferimento alla documentazione esterna.
Geert

5
Penso che sia meglio se puoi modificare la risposta accettata e aggiungerla lì
Martin Massera il

12
window.postMessage possiamo usare solo se siamo in grado di accedere sia all'elemento genitore (la nostra pagina HTML) che all'elemento figlio (altro iframe di dominio). Altrimenti "NON C'È POSSIBILITÀ", genererà sempre un errore "DOMException non bloccato: bloccato un frame con origine "< nomedominio.com >" dall'accesso a un frame di origine incrociata. "
VIJAY P

19

Controlla la http://www.<domain>.comconfigurazione del server web del dominio per X-Frame-Options È una funzione di sicurezza progettata per prevenire attacchi click-jacking,

Come funziona il clickjacking?

  1. La pagina malvagia appare esattamente come la pagina della vittima.
  2. Quindi ha indotto gli utenti a inserire nome utente e password.

Tecnicamente il male ha una iframefonte con la pagina della vittima.

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

Come funziona la funzione di sicurezza

Se si desidera impedire il rendering della richiesta del server Web all'interno di un iframeaggiungere le opzioni x-frame

X-Frame-Options DENY

Le opzioni sono:

  1. SAMEORIGIN // consente solo al mio dominio di rendere il mio HTML all'interno di un iframe.
  2. DENY // non consente il rendering del mio HTML all'interno di alcun iframe
  3. "ALLOW-FROM https://example.com/ " // consente a un dominio specifico di eseguire il rendering del mio HTML all'interno di un iframe

Questo è l'esempio di configurazione di IIS:

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

La soluzione alla domanda

Se il server Web ha attivato la funzione di sicurezza, potrebbe causare un SecurityError sul lato client come dovrebbe.


1
Non penso che X-Frame-Options si applichi qui - X-Frame-Options definito dalla pagina guest (incorporata) può far sì che il genitore rifiuti di caricare la pagina, ma per quanto ne so non influisce su javascript access - anche con X-Frame-Options: *, non credo che sarai in grado di accedere al DOM di una pagina guest di origine diversa con javascript
Noah Gilmore,

13

Per me volevo implementare una stretta di mano a 2 vie, il che significa:
- la finestra del genitore si caricherà più velocemente dell'iframe
- l'iframe dovrebbe parlare con la finestra del genitore non appena è pronto
- il genitore è pronto a ricevere il messaggio iframe e riprodurre

questo codice viene utilizzato per impostare l'etichetta bianca nell'iframe utilizzando il codice [proprietà personalizzata CSS]
:
iframe

$(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","*");
    }
});

genitore

$(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);
});

naturalmente puoi limitare le origini e il testo, questo è un codice facile da lavorare con cui
ho trovato utile questo esame:
[Messaggistica tra domini con postMessage]


ho a che fare con un problema con Safari in cui il documento in iframe sta eseguendo il suo JS più tardi della pagina principale che causa l'invio del messaggio prima che il documento in iframe stia ascoltando i messaggi; che è esattamente l'opposto di quello che fanno Chrome e Firefox: hai testato il tuo codice in Safari su iOS? btw postMessage con il secondo parametro del valore "*" non è del tutto sicuro, dovresti sempre specificare domain
sKopheK

Il tuo primo blocco di codice è quello sull'iframe nel genitore o è nella pagina che viene caricato nell'iframe?
Demonic218,

0

Vorrei aggiungere la configurazione specifica di Java Spring che può influire su questo.

Nel sito Web o nell'applicazione Gateway è presente un'impostazione contentSecurityPolicy

in primavera è possibile trovare l'implementazione della sottoclasse 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)

Il browser verrà bloccato se non si è definito qui un contenitore esterno sicuro.


0

Se hai il controllo sul contenuto dell'iframe, ovvero se viene semplicemente caricato in una configurazione di origine incrociata come su Amazon Mechanical Turk, puoi aggirare questo problema con l' <body onload='my_func(my_arg)'>attributo per l'html interno.

Ad esempio, per l'html interno, utilizzare il thisparametro html (yes - thisè definito e si riferisce alla finestra padre dell'elemento body interno):

<body onload='changeForm(this)'>

Nell'html interno:

    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>

-25
  • Apri il menu Start
  • Digita windows + R o apri "Esegui
  • Eseguire il comando seguente.

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


3
Ottimo per test rapidi e sporchi!
user1068352

6
Terribile per tutto ciò che non è un test rapido e sporco ... e già indirizzato nella risposta accettata.
Quentin,

2
Anche con il comando, non funziona perché Chrome evita di disabilitare la sicurezza Web in questo modo
Metafaniel
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.