SecurityError: заблоковано кадр з початком доступу до кадру з перехресним походженням


555

Я завантажую <iframe>свою сторінку на HTML і намагаюся отримати доступ до елементів всередині неї за допомогою Javascript, але коли я намагаюся виконати свій код, я отримую таку помилку:

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

Чи можете ви допомогти мені знайти рішення, щоб я мав доступ до елементів у кадрі?

Я використовую цей код для тестування, але даремно:

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

Відповіді:


820

Політика одного походження

Ви не можете отримати доступ до <iframe>іншого походження за допомогою JavaScript, це було б величезним недоліком безпеки, якби ви могли це зробити. Для веб-переглядачів одного і того ж джерела блокують сценарії, намагаючись отримати доступ до кадру з іншим походженням .

Походження вважається різним, якщо хоча б одна з наступних частин адреси не підтримується:

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

Протокол , ім'я хоста та порт повинні бути однаковими для вашого домену, якщо ви хочете отримати доступ до кадру.

ПРИМІТКА. Відомо, що Internet Explorer не суворо дотримується цього правила, детальну інформацію див. Тут .

Приклади

Ось що буде, намагаючись отримати доступ до таких URL-адрес 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 

Обхід

Навіть незважаючи на те, що політика одного і того ж джерела блокує скрипти для доступу до вмісту веб-сайтів з різним походженням, якщо ви володієте обома сторінками, ви можете вирішити цю проблему, використовуючи window.postMessageта її відносну messageподію, щоб надсилати повідомлення між двома сторінками, наприклад:

  • На головній сторінці:

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

    Другим аргументом postMessage()може бути '*'вказівка ​​на відсутність переваг щодо походження місця призначення. Завжди потрібно надати цільове джерело, коли це можливо, щоб не розголошувати дані, які ви надсилаєте на будь-який інший сайт.

  • У вашому <iframe>(міститься на головній сторінці):

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

Цей метод можна застосовувати в обох напрямках , створюючи слухача на головній сторінці і отримуючи відповіді з кадру. Така ж логіка може бути реалізована і в спливаючих вікнах, і в основному в будь-якому новому вікні, створеному головною сторінкою (наприклад, за допомогою window.open()), без різниці.

Вимкнення політики того самого походження у вашому браузері

На цю тему вже є хороші відповіді (я щойно знайшов їх гугл), тож для браузерів, де це можливо, я зв’яжу відносну відповідь. Однак пам’ятайте, що відключення політики того самого походження вплине лише на ваш веб-переглядач . Також запуск браузера з відключеними налаштуваннями безпеки однакового походження надає будь-якому веб-сайту доступ до ресурсів перехресного походження, тому це дуже небезпечно і НІКОЛИ не робити це, якщо ви не знаєте, що саме робите (наприклад, цілі розробки) .


27
Будь-яка інша відповідь, яку я знайшов 1 , 2 , говорить про те, що CORS / Access-Control-Allow-Originне застосовується до iFrames, лише до XHR, Fonts, WebGL таcanvas.drawImage . Я вважаю, що postMessageце єдиний варіант.
snappieT

369
Перший раз я бачив оператора tilde "~" у javascript. Для всіх, хто також не знав, що це робить: він перетворює -1 в 0, що дозволяє економити вам, що потрібно робити "! = -1" за результатом indexOf. Особисто я думаю, що я продовжуватиму використовувати!
Редзарф

4
@SabaAhang просто перевірте наявність iframe.src, і якщо веб-сайт відрізняється від імені хоста вашого домену, ви не можете отримати доступ до цього кадру.
Марко Бонеллі

17
@Snuggs абсолютно неправильно, ~повертає доповнення числа 2, таким чином nстає -n-1, тобто означає, що -1стане лише 0(що трактується як false), а будь-яке інше значення пройде тест. IE 0 = -(-1)-1, ні -(-1+1).
Марко Бонеллі

2
@ user2568374 location.ancestorOrigins[0]- це місце батьківського кадру. Якщо рамка працює в іншому місці , і ви перевірити з допомогою event.origin.indexOf(location.ancestorOrigins[0])ви перевіряєте , якщо походження події містить кадр адреса батька, який завжди збирається бутиtrue , тому ви дозволяєте будь-якому батькові з будь-яким походженням , щоб отримати доступ до кадру, і це це, очевидно, не те, що ти хочеш зробити. Більше того, document.referrerце і погана практика, як я вже пояснював у коментарях вище.
Марко Бонеллі

55

Доповнення відповіді Марко Бонеллі: найкращим поточним способом взаємодії кадрів / кадрів є використання window.postMessage, що підтримується всіма браузерами


21
Хоча це посилання може відповісти на питання, краще включити сюди суттєві частини відповіді та надати посилання для довідки. Відповіді лише на посилання можуть стати недійсними, якщо пов’язана сторінка зміниться. - З огляду
Алессандро Каттін

9
Я не згоден, @AlessandroCuttin. Пояснення, як window.postMessageпрацює, лише дублює прийняту відповідь, на яку я вже посилаюся. Крім того, важливою цінністю моєї відповіді є саме те, що вона посилається на зовнішню документацію.
Geert

5
Я думаю, що це краще, якщо ви зможете відредагувати прийняту відповідь і додати її туди
Мартін Массера

12
window.postMessage ми можемо використовувати лише в тому випадку, якщо ми зможемо отримати доступ до батьківського (нашого HTML-сторінки) та дочірнього елемента (інший iframe домену). Інакше "НЕ МОЖЛИВОСТІ", він завжди видає помилку "Uncaught DOMException: Заблокований кадр з початком "< yourdomainname.com >" через доступ до кадру з перехресним походженням. "
VIJAY P

18

Перевірте http://www.<domain>.comконфігурацію веб-сервера домену. X-Frame-Options Це функція безпеки, призначена для запобігання атакам clickJacking,

Як працює clickJacking?

  1. Зла сторінка виглядає точно як сторінка жертви.
  2. Потім він обманув користувачів ввести своє ім’я користувача та пароль.

Технічно зло має iframeджерело на сторінці жертви.

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

Як працює функція безпеки

Якщо ви хочете не допустити надання запиту веб-сервера в межах, iframeдодайте параметри x-frame

Параметри X-кадру ДЕНЬ

Варіанти:

  1. SAMEORIGIN // дозволяють лише моєму домену відтворювати мій HTML всередині iframe.
  2. DENY // не дозволяють моєму HTML виводитися всередині будь-якої рамки
  3. "ДОЗВОЛИТИ З https://example.com/ " // дозволити конкретному домену візуалізувати мій HTML всередині iframe

Це приклад конфігурації IIS:

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

Розв’язання питання

Якщо веб-сервер активував функцію захисту, це може спричинити клієнтський SecurityError як слід.


1
Я не думаю, що тут застосовуються параметри X-Frame - Параметри X-Frame, визначені гостьовою (вбудованою) сторінкою, можуть змусити батьків відмовитись завантажувати сторінку, але, наскільки я знаю, це не впливає на JavaScript доступ - навіть з параметрами X-Frame: *, я не думаю, що ви не зможете отримати доступ до DOM гостьової сторінки іншого походження за допомогою javascript
Ноа Гілмор

13

Для мене я хотів реалізувати двосторонній рукостискання, тобто:
- батьківське вікно завантажиться швидше, ніж iframe
- iframe повинен поговорити з батьківським вікном, як тільки воно буде готове
- батько готовий отримати повідомлення iframe та повторити

цей код використовується для встановлення білої мітки в iframe, використовуючи [CSS custom властивість]
код:
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","*");
    }
});

батьківський

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

звичайно, ви можете обмежити походження та текст. Це легко в роботі - з кодом.
Я вважав, що цей Examlpe є корисним:
[Повідомлення між доменними повідомленнями з постмесажем]


Я маю справу з проблемою із сафарі, коли документ у iframe виконує свій JS пізніше батьківської сторінки, що призводить до того, що повідомлення буде надіслане раніше, ніж документ у iframe прослуховує повідомлення; що прямо протилежне тому, що роблять хром і firefox - чи ви протестували свій код у сафарі на ios? btw postMessage з другим параметром значення "*" не зовсім безпечний, завжди слід вказувати домен
sKopheK

Ваш перший блок коду - це те, що в iframe в батьківському вікні чи це на сторінці, яка завантажується в iframe?
Demonic218

0

Я хотів би додати специфічну конфігурацію Java Spring, яка може впливати на це.

На веб-сайті або в шлюзі є налаштування contentSecurityPolicy

навесні ви можете знайти реалізацію підкласу 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)

Браузер буде заблокований, якщо ви тут не визначили безпечний зовнішній контекст.


0

Якщо у вас є контроль над вмістом iframe - тобто, якщо він просто завантажений у налаштуваннях перехресного походження, таких як Amazon Mechanical Turk - ви можете обійти цю проблему з <body onload='my_func(my_arg)'>атрибутом внутрішнього html.

Наприклад, для внутрішнього html використовуйте thisпараметр html (так - thisвизначено, і воно посилається на батьківське вікно внутрішнього елемента тіла):

<body onload='changeForm(this)'>

У внутрішньому 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>

-24
  • Відкрийте меню "Пуск"
  • Введіть windows + R або відкрийте «Виконати
  • Виконайте таку команду.

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


3
Добре для швидкого і брудного тесту!
користувач1068352

6
Жахливе для будь-чого, що не є швидким і брудним тестом ... і вже звертайтеся у прийнятій відповіді.
Квентін

2
Навіть із командою це не працює, оскільки Chrome уникає відключення веб-безпеки таким чином
Metafaniel
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.