SecurityError: impediu que um quadro com origem acesse um quadro de origem cruzada

556

Estou carregando um <iframe>na minha página HTML e tentando acessar os elementos dentro dele usando Javascript, mas quando tento executar meu código, recebo o seguinte erro:

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

Você pode me ajudar a encontrar uma solução para que eu possa acessar os elementos no quadro?

Estou usando esse código para teste, mas em vão:

$(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
fonte

Respostas:

821

Política de mesma origem

Você não pode acessar um <iframe>com origem diferente usando JavaScript, seria uma enorme falha de segurança se você pudesse fazê-lo. Para os navegadores de política de mesma origem, os scripts de bloqueio tentam acessar um quadro com uma origem diferente .

A origem é considerada diferente se pelo menos uma das seguintes partes do endereço não for mantida:

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

Protocolo , nome do host e porta devem ser iguais ao seu domínio, se você quiser acessar um quadro.

NOTA: Sabe-se que o Internet Explorer não segue rigorosamente esta regra, consulte aqui para obter detalhes.

Exemplos

Aqui está o que aconteceria ao tentar acessar os seguintes URLs em 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 

Gambiarra

Embora a política de mesma origem impeça os scripts de acessar o conteúdo de sites com uma origem diferente, se você possui as duas páginas, poderá solucionar esse problema usando window.postMessagee seu messageevento relativo para enviar mensagens entre as duas páginas, desta forma:

  • Na sua página principal:

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

    O segundo argumento para postMessage()pode ser '*'para não indicar preferência sobre a origem do destino. Uma origem de destino sempre deve ser fornecida quando possível, para evitar a divulgação dos dados que você envia para qualquer outro site.

  • No seu <iframe>(contido na página principal):

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

Esse método pode ser aplicado nas duas direções , criando também um ouvinte na página principal e recebendo respostas do quadro. A mesma lógica também pode ser implementada em pop-ups e basicamente qualquer nova janela gerada pela página principal (por exemplo, usando window.open()) também, sem nenhuma diferença.

Desativando a política de mesma origem no seu navegador

Já existem boas respostas sobre esse tópico (acabei de encontrá-las no Google). Portanto, para os navegadores onde isso for possível, vincularemos a resposta relativa. No entanto, lembre-se de que desativar a política de mesma origem afetará apenas o seu navegador . Além disso, a execução de um navegador com configurações de segurança da mesma origem desabilitadas concede a qualquer site acesso a recursos de origem cruzada, portanto , é muito inseguro e NUNCA deve ser feito se você não souber exatamente o que está fazendo (por exemplo, fins de desenvolvimento) .

Marco Bonelli
fonte
27
Qualquer outra resposta que encontrei 1 , 2 sugere que o CORS / Access-Control-Allow-Originnão se aplica aos iFrames, apenas aos XHRs, Fonts, WebGL ecanvas.drawImage . Eu acredito que postMessageé a única opção.
precisa saber é o seguinte
369
Primeira vez que vi o operador til "~" em javascript. Para qualquer pessoa que também não sabia o que faz: converte -1 em 0, o que evita que você precise "! = -1" no resultado do indexOf. Pessoalmente, acho que continuarei usando "! = -1", pois é mais fácil para outros programadores entenderem e evitarem os bugs que viriam do esquecimento de colocar o til. (Mas é sempre bom aprender algo novo.)
Redzarf
4
O @SabaAhang apenas verifique o iframe.srce, se o site for diferente do nome do host do seu domínio, você não poderá acessar esse quadro.
Marco Bonelli
18
@ Snuggs totalmente errado, ~retorna o complemento 2 do número, ntornando - se -n-1, o que significa que apenas -1se tornará 0(que é interpretado como false) e que qualquer outro valor passará no teste. Ou seja 0 = -(-1)-1, não -(-1+1).
Marco Bonelli 8/16
2
@ user2568374 location.ancestorOrigins[0]é o local do quadro pai. Se o seu quadro estiver sendo executado em outro site e você verificar usando, event.origin.indexOf(location.ancestorOrigins[0])está verificando se a origem do evento contém o endereço do quadro dos pais, o que sempre serátrue , portanto, você está permitindo que qualquer pai com qualquer origem acesse seu quadro, e isso obviamente não é algo que você deseja fazer. Além disso,document.referrer é uma prática ruim também, como já expliquei nos comentários acima.
Marco Bonelli
55

Complementando a resposta de Marco Bonelli: a melhor maneira atual de interagir entre quadros / iframes está usando window.postMessage, suportada por todos os navegadores

Geert
fonte
21
Embora esse link possa responder à pergunta, é melhor incluir aqui as partes essenciais da resposta e fornecer o link para referência. As respostas somente para links podem se tornar inválidas se a página vinculada for alterada. - Da avaliação
Alessandro Cuttin
9
Eu discordo, @AlessandroCuttin. Explicar como window.postMessagefunciona duplicaria apenas a resposta aceita, que eu já mencionei. Além disso, o valor essencial que minha resposta agrega é exatamente o de referenciar documentação externa.
Geert
5
Eu acho que é melhor se você pode editar a resposta aceita e adicioná-lo lá
Martin Massera
12
window.postMessage, podemos usar apenas se pudermos acessar o elemento pai (nossa página HTML) e o filho (outro domínio iframe). Caso contrário, "NÃO HÁ POSSIBILIDADE", ele sempre emitirá um erro "DOM não detectado: Exceção de domínio bloqueada" com origem "< yourdomainname.com >" de acessar um quadro de origem cruzada. "
VIJAY P
19

Verifique a http://www.<domain>.comconfiguração do servidor Web do domínio. X-Frame-Options É um recurso de segurança projetado para impedir ataques de clickJacking,

Como funciona o clickJacking?

  1. A página do mal se parece exatamente com a página da vítima.
  2. Em seguida, enganou os usuários para inserir seu nome de usuário e senha.

Tecnicamente, o mal tem uma iframefonte na página da vítima.

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

Como o recurso de segurança funciona

Se você deseja impedir que a solicitação do servidor da Web seja renderizada em um campo, iframeadicione as opções x-frame

Opções de quadro X DENY

As opções são:

  1. SAMEORIGIN // permite apenas que meu próprio domínio processe meu HTML dentro de um iframe.
  2. DENY // não permite que meu HTML seja renderizado em nenhum iframe
  3. "PERMITIDO https://example.com/ " // permite que um domínio específico processe meu HTML dentro de um iframe

Este é o exemplo de configuração do IIS:

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

A solução para a pergunta

Se o servidor da Web ativou o recurso de segurança, ele pode causar um SecurityError do lado do cliente como deveria.

Shahar Shokrani
fonte
1
Eu não acho que as X-Frame-Options se apliquem aqui - as X-Frame-Options definidas pela página de convidado (incorporada) podem fazer com que o pai se recuse a carregar a página, mas, tanto quanto sei, isso não afeta o javascript acesso - mesmo com X-frame-Options: *, eu não acho que você vai ser capaz de acessar o DOM de uma página convidado origem diferente com javascript
Noah Gilmore
13

Para mim, eu queria implementar um handshake bidirecional, o que significa:
- a janela pai carregará mais rápido que o iframe
- o iframe deve conversar com a janela pai assim que
estiver pronto - o pai está pronto para receber a mensagem e a repetição do iframe

esse código é usado para definir etiqueta branca no iframe usando o código [CSS custom property]
:
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","*");
    }
});

pai

$(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, você pode limitar as origens e o texto; este é um código fácil de trabalhar;
achei este exame útil:
[Mensagens entre domínios com postMessage]

Yakir Manor
fonte
Estou lidando com um problema no safari em que o documento no iframe está executando seu JS mais tarde que a página pai, o que faz com que a mensagem seja enviada mais cedo do que o documento no iframe está ouvindo mensagens; que é exatamente o oposto do que o chrome e o firefox fazem - você testou seu código no safari no ios? btw postMessage com o segundo parâmetro de valor "*" não é bastante seguro, você deve sempre especificar domínio
sKopheK
Seu primeiro bloco de código está no iframe no pai ou na página carregada no iframe?
Demonic218
0

Gostaria de adicionar uma configuração específica do Java Spring que possa afetar isso.

No site ou aplicativo de gateway, há uma configuração contentSecurityPolicy

no Spring, você pode encontrar a implementação da subclasse 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)

O navegador será bloqueado se você não definir aqui o conteúdo externo seguro.

ssp
fonte
0

Se você tiver controle sobre o conteúdo do iframe, ou seja, se ele for carregado apenas em uma configuração de origem cruzada, como no Amazon Mechanical Turk, poderá contornar esse problema com o <body onload='my_func(my_arg)'>atributo do html interno.

Por exemplo, para o html interno, use o thisparâmetro html (yes - thisé definido e se refere à janela pai do elemento do corpo interno):

<body onload='changeForm(this)'>

No 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>
Zhanwen Chen
fonte
-25
  • Abra o menu Iniciar
  • Digite windows + R ou abra "Executar
  • Execute o seguinte comando.

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

sakthi sudhan
fonte
3
Bom para teste rápido e sujo!
user1068352
6
Terrível para qualquer coisa que não seja um teste rápido e sujo… e já considere a resposta aceita.
Quentin
2
Mesmo com o comando, ele Indiferente trabalho, porque Chrome evita desativando a segurança web desta forma
Metafaniel