【實作紀錄】如何與 iframe 傳值與溝通 - PostMessage

Ray
Jun 15, 2022

--

圖片來源

父層打開了一個iframe,但要如何與它溝通?還有子層iframe又要如何傳訊息給將它嵌入的父層?PostMessage可以解決這個問題。

介紹:

父層佈署在AWS上,嵌入一個佈署在Github Pages的子層網址。利用PostMessage來溝通。(DEMO網址)

核心概念:

1.創建相對瀏覽器窗口對象(父層/子層)

2.透過postmessage對窗口對象發送訊息 / 透過EventListener監聽message事件,取得窗口對象訊息

父層

1.在HTML中建立要嵌入的iframe,並取名為iframeChild

<iframe name="iframeChild"></iframe>

2.在TS中透過window.open取得子層窗口對象,透過剛剛建立的iframe打開

this.iframeChild = window.open('https://1mid.github.io/childIframe/', 'iframeChild');

3.透過子層窗口對象的postmessage方法對子層發送訊息

this.iframeChild?.postMessage({ type: 'iframeChannel', message: msg }, '*');

最後一個參數為targetOrigin,設’*’代表不檢查。在發送消息的時候,如果目標窗口的協議、主機地址或端口這三者的任意一項不匹配targetOrigin提供的值,那麼消息就不會被發送。可以避免被爛用

4.監聽message事件,監聽來自子層的訊息事件

@HostListener('window:message', ['$event'])onMessage(event: any) { if (event.data?.type === "iframeChannel") {  this.msg += 'child:' + JSON.stringify(event.data.message) + '\n'; }}

event.data.type是為了用來區分開別的應用的message,否則會聽到其他應用的訊息(ex.webpack)。

iframe Parent Source Code

子層

1.取得父層的窗口對象

iframeParent: Window | undefined;this.iframeParent = window.opener as Window;

2.透過父層窗口對象對父層發送訊息

this.iframeParent?.postMessage({ type: 'iframeChannel', message: text }, '*');

3.監聽message事件,監聽來自父層的訊息事件

@HostListener('window:message', ['$event'])onMessage(event: any) { if (event.data?.type === "iframeChannel") {  this.msg += 'parent:' + JSON.stringify(event.data.message) + '\n'; }}

iframe Child Source Code

補充

ios手機不觸發onLoad事件(2023/11/20)

<iframe onload = "onload()"></iframe>

目前觀察到若是非同源的網站互相嵌入,手機系統是ios開啟網站時,有放置iframe的頁面onLoad事件不會被觸發(但是Android可以觸發)。

解決方法是透過被嵌入的網站以postMessage的方式告知主頁已經init完畢。

(這問題真的很搞)

實作測試

iframe Child已經建好了,可以被任何父層透過window.open的方式嵌入,所以可以嘗試自己建一個iframe Parent用window.open的方式打開https://1mid.github.io/childIframe/,測試看看是否能正常溝通。

iframe要如何拿到父層的地址?

var referrer = document.referrer; //取得route過來的前地址(MDN)

iframe的限制

若子層的iframe有使用session,則這個session並不會存在iframe,而是會存在父層,這時如果子層需要取得session的時候就會拿不到,是因為瀏覽器安全性政策的關係,被嵌入iframe無法取得父層的session,避免CSRF。

如果iframe可以取得session會發生什麼事?

就可以透過放一個隱藏的iframe,並讓程式自己去submit,這樣就能偽造該使用者的身分,去執行一些操作。

<iframe style="display:none" name="csrf-frame"></iframe> <form method='POST' action='https://small-min.blog.com/delete' target="csrf-frame" id="csrf-form">   
<input type='hidden' name='id' value='3'>
<input type='submit' value='submit'>
</form>
<script>document.getElementById("csrf-form").submit()</script>

參考:讓我們來談談 CSRF

--

--

No responses yet