SecurityError: bloqueó un marco con origen para acceder a un marco de origen cruzado

556

Estoy cargando un archivo <iframe>en mi página HTML e intento acceder a los elementos que contiene mediante Javascript, pero cuando intento ejecutar mi código, aparece el siguiente error:

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

¿Pueden ayudarme a encontrar una solución para poder acceder a los elementos en el marco?

Estoy usando este código para probar, pero en vano:

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

Respuestas:

821

Política del mismo origen

No puede acceder a un <iframe>origen diferente usando JavaScript, sería un gran defecto de seguridad si pudiera hacerlo. Para la política del mismo origen, los navegadores bloquean los scripts que intentan acceder a un marco con un origen diferente .

El origen se considera diferente si no se mantiene al menos una de las siguientes partes de la dirección:

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

El protocolo , el nombre de host y el puerto deben ser los mismos de su dominio, si desea acceder a un marco.

NOTA: se sabe que Internet Explorer no sigue estrictamente esta regla, consulte aquí para más detalles.

Ejemplos

Esto es lo que sucedería al intentar acceder a las siguientes URL desde 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 

Solución alterna

Aunque la política del mismo origen bloquea el acceso de los scripts al contenido de sitios con un origen diferente, si posee ambas páginas, puede solucionar este problema utilizando window.postMessagey su messageevento relativo para enviar mensajes entre las dos páginas, de esta manera:

  • En tu página principal:

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

    El segundo argumento postMessage()puede ser '*'indicar que no hay preferencia sobre el origen del destino. Siempre se debe proporcionar un origen de destino cuando sea posible, para evitar revelar los datos que envía a cualquier otro sitio.

  • En su <iframe>(contenido en la 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; 
        } 
    }); 
    

Este método se puede aplicar en ambas direcciones , creando un oyente en la página principal y recibiendo respuestas del marco. La misma lógica también se puede implementar en ventanas emergentes y, básicamente, en cualquier ventana nueva generada por la página principal (por ejemplo, usando window.open()), sin ninguna diferencia.

Deshabilitar la política del mismo origen en su navegador

Ya hay algunas buenas respuestas sobre este tema (las acabo de buscar en Google), por lo que, para los navegadores donde sea posible, vincularé la respuesta relativa. Sin embargo, recuerde que deshabilitar la política del mismo origen solo afectará a su navegador . Además, ejecutar un navegador con la configuración de seguridad del mismo origen deshabilitada otorga a cualquier sitio web acceso a recursos de origen cruzado, por lo que es muy inseguro y NUNCA debe hacerse si no sabe exactamente lo que está haciendo (por ejemplo, con fines de desarrollo) .

Marco Bonelli
fuente
27
Cualquier otra respuesta que haya encontrado 1 , 2 sugiere que CORS / Access-Control-Allow-Originno se aplica a iFrames, solo a XHR, Fonts, WebGL ycanvas.drawImage . Creo que postMessagees la única opción.
SnappieT
370
La primera vez que vi el operador tilde "~" en javascript. Para cualquier otra persona que tampoco supiera lo que hace: convierte -1 a 0, lo que le ahorra tener que hacer "! = -1" en el resultado del indexOf. Personalmente, creo que seguiré usando "! = -1", ya que es más fácil de entender para otros programadores y evita los errores que surgen al olvidar poner la tilde. (Pero siempre es bueno aprender algo nuevo).
Redzarf
44
@SabaAhang solo verifique el iframe.src, y si el sitio es diferente del nombre de host de su dominio, entonces no puede acceder a ese marco.
Marco Bonelli
18
@Snuggs está totalmente equivocado, ~devuelve el complemento de 2 del número, por lo que se nconvierte -n-1, lo que significa que solo -1se convertirá 0(lo que se interpreta como false), y cualquier otro valor pasará la prueba. IE 0 = -(-1)-1, no -(-1+1).
Marco Bonelli el
2
@ user2568374 location.ancestorOrigins[0]es la ubicación del marco primario. Si su marco se está ejecutando dentro de otro sitio y lo verifica utilizando event.origin.indexOf(location.ancestorOrigins[0]), está verificando si el origen del evento contiene la dirección del marco del padre, que siempre serátrue , por lo tanto, está permitiendo que cualquier padre con cualquier origen acceda a su marco, y esto obviamente no es algo que quieras hacer. Además, también document.referreres una mala práctica, como ya expliqué en los comentarios anteriores.
Marco Bonelli
55

Complementando la respuesta de Marco Bonelli: está utilizando la mejor forma actual de interactuar entre marcos / iframes window.postMessage, compatible con todos los navegadores

Geert
fuente
21
Si bien este enlace puede responder la pregunta, es mejor incluir aquí las partes esenciales de la respuesta y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden volverse inválidas si la página vinculada cambia. - De la opinión
Alessandro Cuttin
99
No estoy de acuerdo, @AlessandroCuttin. Explicar cómo window.postMessagefunciona solo duplicaría la respuesta aceptada a la que ya me refiero. Además, el valor esencial que agrega mi respuesta es exactamente el de hacer referencia a documentación externa.
Geert
55
Creo que es mejor si puedes editar la respuesta aceptada y agregarla allí
Martin Massera
12
window.postMessage solo podemos usar si podemos acceder tanto al elemento primario (nuestra página HTML) como al elemento secundario (otro iframe de dominio). De lo contrario, "NO HAY POSIBILIDAD", siempre arrojará un error "Excepción DOM no capturada: bloqueó un marco con el origen "< yourdomainname.com >" desde el acceso a un marco de origen cruzado ".
VIJAY P
19

Verifique la http://www.<domain>.comconfiguración del servidor web del dominio. X-Frame-Options Es una característica de seguridad diseñada para evitar ataques de clickJacking,

¿Cómo funciona clickjacking?

  1. La página del mal se ve exactamente como la página de la víctima.
  2. Luego engañó a los usuarios para que ingresaran su nombre de usuario y contraseña.

Técnicamente el mal tiene una iframefuente con la página de la víctima.

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

Cómo funciona la característica de seguridad

Si desea evitar que la solicitud del servidor web se procese dentro de un iframeagregar las opciones x-frame-

X-Frame-Options DENY

Las opciones son:

  1. SAMEORIGIN // permitir solo que mi propio dominio represente mi HTML dentro de un iframe.
  2. DENY // no permita que mi HTML se represente dentro de ningún iframe
  3. "ALLOW-FROM https://example.com/ " // permitir que un dominio específico represente mi HTML dentro de un iframe

Este es el ejemplo de configuración de IIS:

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

La solución a la pregunta.

Si el servidor web activó la función de seguridad, puede causar un SecurityError del lado del cliente como debería.

Shahar Shokrani
fuente
1
No creo que las opciones de marco X se apliquen aquí: las opciones de marco X definidas por la página invitada (incrustada) pueden hacer que el padre se niegue a cargar la página, pero que yo sepa, no afecta a JavaScript acceso, incluso con X-Frame-Options: *, no creo que pueda acceder al DOM de una página de invitado de origen diferente con javascript
Noah Gilmore
13

Para mí, quería implementar un protocolo de enlace bidireccional, lo que significa:
- la ventana principal se cargará más rápido que el iframe
- el iframe debería hablar con la ventana principal tan pronto como esté listo
- el padre está listo para recibir el mensaje del iframe y reproducirlo

este código se usa para establecer la etiqueta blanca en el iframe usando el código [propiedad personalizada 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","*");
    }
});

padre

$(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, puede limitar los orígenes y el texto, este es un código fácil de trabajar.
Este examen me pareció útil:
[Mensajes entre dominios con postMessage]

Mansión Yakir
fuente
Estoy lidiando con un problema con safari donde el documento en iframe está ejecutando su JS más tarde que la página principal, lo que hace que el mensaje se envíe antes que el documento en iframe esté escuchando mensajes; que es exactamente lo contrario de lo que hacen Chrome y Firefox: ¿ha probado su código en Safari en iOS? por cierto postMessage con el segundo parámetro de valor "*" no es del todo seguro, siempre debe especificar el dominio
sKopheK
Su primer bloque de código, ¿está en el iframe en el padre o está en la página que se carga en el iframe?
Demonic218
0

Me gustaría agregar la configuración específica de Java Spring que pueda afectar esto.

En el sitio web o la aplicación Gateway hay una configuración contentSecurityPolicy

en Spring puedes encontrar la implementación de la subclase 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)

El navegador se bloqueará si no ha definido un contenido externo seguro aquí.

ssp
fuente
0

Si tiene control sobre el contenido del iframe, es decir, si simplemente se carga en una configuración de origen cruzado como en Amazon Mechanical Turk, puede sortear este problema con el <body onload='my_func(my_arg)'>atributo para el html interno.

Por ejemplo, para el html interno, use el thisparámetro html (sí, thisestá definido y se refiere a la ventana principal del elemento del cuerpo interno):

<body onload='changeForm(this)'>

En el 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
fuente
-25
  • Abre el menú de inicio
  • Escriba windows + R o abra "Ejecutar
  • Ejecute el siguiente comando.

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

sakthi sudhan
fuente
3
¡Bueno para una prueba rápida y sucia!
user1068352
66
Terrible para cualquier cosa que no sea una prueba rápida y sucia ... y que ya se aborde en la respuesta aceptada.
Quentin
2
Incluso con el comando, no funciona porque Chrome evita deshabilitar la seguridad web de esta manera
Metafaniel