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

556

Я загружаю <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!";
    });
});
mubashermubi
источник

Ответы:

821

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

Вы не можете получить доступ к <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
370
Впервые я увидел тильду "~" в javascript. Для всех, кто также не знал, что он делает: он конвертирует -1 в 0, что избавляет вас от необходимости делать "! = -1" в результате indexOf. Лично я думаю, что я продолжу использовать "! = -1", так как другим программистам будет легче понять и избежать ошибок, которые могут возникнуть из-за забвения тильды. (Но всегда приятно узнавать что-то новое.)
Редзарф
4
@SabaAhang просто проверьте iframe.src, и если сайт отличается от имени хоста вашего домена, вы не сможете получить доступ к этому фрейму.
Марко Бонелли
18
@Snuggs совершенно неверно, ~возвращает 2-е дополнение числа, так nстановится -n-1, то есть становится , что только -1станет 0(что интерпретируется как false), и любое другое значение пройдет тест. То есть 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работает, только дублирует принятый ответ, на который я уже ссылаюсь. Более того, существенная ценность, которую добавляет мой ответ, заключается именно в ссылке на внешнюю документацию.
Герт
5
Я думаю, будет лучше, если вы сможете отредактировать принятый ответ и добавить его туда
Мартин Массера,
12
window.postMessage мы можем использовать только в том случае, если сможем получить доступ как к родительскому (наша HTML-страница), так и к дочернему элементу (iframe другого домена). В противном случае «НЕТ ВОЗМОЖНОСТИ», он всегда будет выдавать ошибку «Uncaught DOMException: Blocked frame» с источником "< yourdomainname.com >" от доступа к фрейму перекрестного происхождения. "
VIJAY P
19

Проверьте 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-options

X-Frame-Options DENY

Варианты:

  1. SAMEORIGIN // разрешить только моему собственному домену отображать мой HTML внутри iframe.
  2. DENY // не разрешаем отображать мой HTML внутри любого iframe
  3. «ALLOW-FROM https://example.com/ » // разрешить конкретному домену отображать мой HTML внутри iframe

Это пример конфигурации IIS:

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

Решение вопроса

Если веб-сервер активировал функцию безопасности, это может вызвать клиентскую ошибку SecurityError, как и должно быть.

Шахар Шокрани
источник
1
Я не думаю, что здесь применимы X-Frame-Options - X-Frame-Options, определенные гостевой (встроенной) страницей, могут заставить родителя отказаться от загрузки страницы, но, насколько я знаю, это не влияет на javascript доступ - даже с X-Frame-Options: *, я не думаю, что вы сможете получить доступ к DOM гостевой страницы другого источника с помощью javascript
Ноа Гилмор
13

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

этот код используется для установки белой метки в iframe с помощью кода [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","*");
    }
});

родитель

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

естественно, вы можете ограничить происхождение и текст, это простой в работе код, который
я нашел полезным для этого примера: [ междоменный обмен
сообщениями с postMessage]

Усадьба Якир
источник
я имею дело с проблемой с сафари, где документ в iframe выполняет свой JS позже, чем родительская страница, из-за чего сообщение отправляется раньше, чем документ в iframe прослушивает сообщения; что совершенно противоположно тому, что делают chrome и firefox - тестировали ли вы свой код в safari на ios? Кстати, 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 (yes - thisопределен и относится к родительскому окну внутреннего элемента body):

<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>
Чжанвэнь Чен
источник
-25
  • Откройте меню «Пуск»
  • Наберите windows + R или откройте «Выполнить»
  • Выполните следующую команду.

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

шакти судхан
источник
3
Хорошо для быстрого и грязного теста!
user1068352
6
Ужасно для всего, что не является быстрым и грязным тестом… и уже обращено в принятом ответе.
Квентин
2
Даже с командой это не работает, потому что Chrome избегает отключения веб-безопасности таким способом
Metafaniel