Javascript - postMessage to sandboxed iframe, почему имя получателя null?

2 вызова postMessage в тесте: 1 с использованием звездочки для targetOrigin, один с использованием одинакового URL-адреса https родительского и дочернего документов.

кнопка 1:

$('.iframed')[0].contentWindow.postMessage( messageData , '*' );

кнопка 2:

$('.iframed')[0].contentWindow.postMessage( messageData , 'https://myurl.net' );

элемент iframe в родительском html-документе, который указывает на дочерний html файл в том же домене, в том же каталоге:

<iframe name="childFrame" class="iframed" src="child.html" sandbox="allow-scripts"></iframe>

оба документа полностью загружены, прежде чем я нажимаю кнопки, чтобы вызвать postMessage.

==========================================

с элементом iframe, написанным, как указано выше, кнопка 1 выполняет postMessage для дочернего iframe и успешно запускает дочерний прослушиватель postMessage (хотя он использует звездочку для targetOrigin, что я предпочел бы не делать). Однако кнопка 2 приводит к следующему ошибка в консоли:

"Не удалось выполнить postMessage для" DOMWindow ": предоставленный целевой источник (' https://myurl.net) не соответствует источнику окон получателя (' null)."

==========================================

если я добавлю "allow-same-origin" в параметры песочницы iframe, обе кнопки затем успешно передадут данные postMessage (без "нулевой" ошибки при вызове postMessage кнопки 2 с URL-адресом, предоставленным для targetOrigin.) однако я не Я не хочу этого делать, так как я использую поведение песочницы iframe, чтобы блокировать содержимое iframe от вызова функций js в родительском документе. это для системы, позволяющей загружать в дочерний iframe "произвольный" контент (html/js/images/pdfs - не исполняемый на сервере, как php).

Следует отметить, что аналогичные кнопки внутри содержимого iframe, которые postMessage для родительского документа работают просто отлично, независимо от параметра allow-same-origin или наличия звездочки /url:

Я подставил кнопку 1:

parent.postMessage( messageData , 'https://myurl.net' ); 

встроенная кнопка 2:

parent.postMessage( messageData , '*' ); 

==========================================

Итак, почему postMessage от родителя к iframe приводит к приведенной выше ошибке, если я не добавляю "allow-same-origin" (и почему эта проблема не влияет на iframe postMessage для родителя)? Я попытался установить для iframe src абсолютный URL-адрес https для документа child.html, но результаты остались прежними. Я также протестировал тот же код на другом сервере, не являющемся ssl-cert, и получил те же результаты (так что не думайте, что он вносит вклад https...). ДОЛЖЕН ли я иметь ЛИБО звездочку в качестве targetOrigin, И/ИЛИ использовать allow-same-origin в параметрах песочницы?

другие разговоры об этой проблеме на SO кажутся тупиковыми, поэтому надеемся получить новый взгляд на решение...

Ответ 1

Проблема создается самим <iframe> с его атрибутом sandbox:

<iframe name="childFrame" class="iframed" src="child.html" sandbox="allow-scripts"></iframe>

В соответствии с документацией Mozilla Developer Network об этом элементе, проблема с политикой одного и того же происхождения возникает из-за следующего:

allow-same-origin: если этот токен не используется, ресурс рассматривается как источник особого происхождения, который всегда не соответствует политике одного источника.

Вы не указали allow-same-origin, это означает, что фрейм обрабатывается как происхождение от специального источника, и вызовы postMessage к этому фрейму завершатся неудачно.

Чтобы решить эту проблему, просто добавьте allow-same-origin в атрибут sandbox, например:

// index.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<iframe name="childFrame" class="iframed" src="child.html" sandbox="allow-scripts allow-same-origin"></iframe>
<button class="btn1">A</button>
<button class="btn2">B</button>

<script>
$('.btn1').click(function(event) {
    // This works!
    $('.iframed')[0].contentWindow.postMessage( "something" , '*' );
});
$('.btn2').click(function(event) {
    // This will work too
    $('.iframed')[0].contentWindow.postMessage( "whatever you want" , 'https://myurl.net' );
});
</script>
// child.html
<script type="text/javascript">
    window.onmessage = function(event) {
        console.log(event.data);
    }
</script>

Это!