window方法:postMessage()

2018-09-03 19:05 更新

postMessage()方法

該window.postMessage()方法安全地啟用Window對(duì)象之間的跨源通信;例如,在頁面和它產(chǎn)生的彈出窗口之間,或者在頁面和嵌入其中的iframe之間。

通常,當(dāng)且僅當(dāng)它們?cè)醋缘捻撁婀蚕硐嗤膮f(xié)議、端口號(hào)和主機(jī)(也稱為“同源策略”)時(shí),允許不同頁面上的腳本相互訪問。window.postMessage()提供一種受控制的機(jī)制來安全地規(guī)避這種限制(如果使用得當(dāng))。

從廣義上講,一個(gè)窗口可以獲得對(duì)另一個(gè)窗口的引用(例如,可以使用targetWindow=window.opener),然后使用targetWindow.postMessage() 在其上發(fā)送一個(gè)MessageEvent。然后,接收窗口可根據(jù)需要自由處理此事件。傳遞給window.postMessage()的參數(shù)(即“message”)通過事件對(duì)象暴露給接收窗口。

postMessage()方法語法

targetWindow .postMessage(message,targetOrigin,[ transfer ]);
targetWindow
對(duì)將接收消息的窗口的引用。獲得此類引用的方法包括:
  • Window.open (生成一個(gè)新窗口然后引用它),
  • Window.opener (引用產(chǎn)生這個(gè)的窗口),
  • HTMLIFrameElement.contentWindow<iframe>從其父窗口引用嵌入式),
  • Window.parent(從嵌入式內(nèi)部引用父窗口<iframe>
  • Window.frames +索引值(命名或數(shù)字)。
message
要發(fā)送到其他窗口的數(shù)據(jù)。使用結(jié)構(gòu)化克隆算法序列化數(shù)據(jù)。這意味著您可以將各種各樣的數(shù)據(jù)對(duì)象安全地傳遞到目標(biāo)窗口,而無需自己序列化。
targetOrigin
指定要調(diào)度的事件的targetWindow的原點(diǎn),可以是文字字符串"*"(表示沒有首選項(xiàng)),也可以是URI。如果在計(jì)劃調(diào)度事件時(shí),targetWindow文檔的方案,主機(jī)名或端口與targetOrigin提供的內(nèi)容不匹配,則不會(huì)調(diào)度該事件;只有當(dāng)所有的三個(gè)條件都匹配時(shí),將調(diào)度該事件。該機(jī)制可以控制發(fā)送消息的位置;例如,如果postMessage()用于傳輸密碼,則該參數(shù)必須是URI,其來源與包含密碼的消息的預(yù)期接收者相同,以防止惡意第三方攔截密碼。始終提供具體的targetOrigin,而不是*,如果您知道其他窗口的文檔應(yīng)該位于何處。未能提供特定目標(biāo)會(huì)泄露您發(fā)送給任何感興趣的惡意站點(diǎn)的數(shù)據(jù)。
transfer(可選的)
是與消息一起傳輸?shù)?code>Transferable對(duì)象序列。這些對(duì)象的所有權(quán)將提供給目標(biāo)端,并且它們?cè)诎l(fā)送端不再可用。

已調(diào)度的事件

otherWindow可以通過執(zhí)行以下JavaScript來偵聽已分派的消息:

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
{
  if (event.origin !== "http://example.org:8080")
    return;

  // ...
}

調(diào)度消息的屬性是:

data
從另一個(gè)窗口傳遞的對(duì)象。
origin
調(diào)用當(dāng)時(shí)發(fā)送消息的窗口的原點(diǎn)postMessage。此字符串是協(xié)議和“://”的串聯(lián),如果存在,則為主機(jī)名,如果存在端口,則“:”后跟端口號(hào),并且與給定協(xié)議的默認(rèn)端口不同。典型起源的例子是https://example.org(意味著端口為443),http://example.net(意味著端口為80)和http://example.com:8080。請(qǐng)注意,此來源保證是該窗口的當(dāng)前或未來來源,該窗口可能已被導(dǎo)航到調(diào)用postMessage后的其他位置。
source
對(duì)發(fā)送消息的window對(duì)象的引用;你可以使用它來建立兩個(gè)不同來源的窗口之間的雙向通信。

安全問題部分

如果您不希望從其他站點(diǎn)接收消息,請(qǐng)不要為message事件添加任何事件偵聽器。這是避免安全問題的完全萬無一失的方法。

如果您確實(shí)希望從其他站點(diǎn)接收消息,則請(qǐng)始終使用origin和可能的source屬性驗(yàn)證發(fā)件人的身份。任何窗口(例如,包括http://evil.example.com)都可以向任何其他窗口發(fā)送消息,并且您無法保證未知發(fā)件人不會(huì)發(fā)送惡意消息。但是,在驗(yàn)證了身份后,您仍應(yīng)始終驗(yàn)證收到的消息的語法。否則,您信任的站點(diǎn)中的安全漏洞只能發(fā)送受信任的消息,然后可以在站點(diǎn)中打開跨站點(diǎn)腳本漏洞。

在postMessage用于將數(shù)據(jù)發(fā)送到其他窗口時(shí),始終指定精確的目標(biāo)原點(diǎn),而不是*。惡意站點(diǎn)可以在您不知情的情況下更改窗口的位置,因此它可以攔截使用postMessage發(fā)送的數(shù)據(jù)。

postMessage()方法示例

/*
 * In window A's scripts, with A being on <http://example.com:8080>:
 */

var popup = window.open(...popup details...);

// When the popup has fully loaded, if not blocked by a popup blocker:

// This does nothing, assuming the window hasn't changed its location.
popup.postMessage("The user is 'bob' and the password is 'secret'",
                  "https://secure.example.net");

// This will successfully queue a message to be sent to the popup, assuming
// the window hasn't changed its location.
popup.postMessage("hello there!", "http://example.com");

function receiveMessage(event)
{
  // Do we trust the sender of this message?  (might be
  // different from what we originally opened, for example).
  if (event.origin !== "http://example.com")
    return;

  // event.source is popup
  // event.data is "hi there yourself!  the secret response is: rheeeeet!"
}
window.addEventListener("message", receiveMessage, false);
/*
 * In the popup's scripts, running on <http://example.com>:
 */

// Called sometime after postMessage is called
function receiveMessage(event)
{
  // Do we trust the sender of this message?
  if (event.origin !== "http://example.com:8080")
    return;

  // event.source is window.opener
  // event.data is "hello there!"

  // Assuming you've verified the origin of the received message (which
  // you must do in any case), a convenient idiom for replying to a
  // message is to call postMessage on event.source and provide
  // event.origin as the targetOrigin.
  event.source.postMessage("hi there yourself!  the secret response " +
                           "is: rheeeeet!",
                           event.origin);
}

window.addEventListener("message", receiveMessage, false);

筆記

無論窗口中文檔的位置如何,任何窗口都可以在任何其他窗口上隨時(shí)訪問此方法,以向其發(fā)送消息。因此,用于接收消息的任何事件偵聽器必須首先使用origin和可能的source屬性檢查消息發(fā)送者的身份。這不容小覷:未能檢查origin和可能的source屬性可以實(shí)現(xiàn)跨站點(diǎn)腳本攻擊。

與任何異步調(diào)度的腳本(超時(shí),用戶生成的事件)一樣,postMessage的調(diào)用者無法檢測(cè)事件處理程序何時(shí)偵聽通過postMessage拋出異常發(fā)送的事件。

postMessage()僅在所有掛起的執(zhí)行上下文完成后才調(diào)度MessageEvent。例如,如果在事件處理程序中調(diào)用postMessage(),則該事件處理程序?qū)⒃贛essageEvent調(diào)度之前運(yùn)行完成,同一事件的任何剩余處理程序也將運(yùn)行。

origin調(diào)度事件的屬性值不受document.domain調(diào)用窗口中當(dāng)前值的影響。

僅對(duì)于IDN主機(jī)名,該origin屬性的值不始終為Unicode或punycode;如果您希望來自IDN站點(diǎn)的消息,請(qǐng)?jiān)谑褂么藢傩詴r(shí)檢查IDN和punycode值的最大兼容性。此值最終將始終為IDN,但是現(xiàn)在您應(yīng)該同時(shí)處理IDN和punycode表單。

當(dāng)發(fā)送窗口包含javascript:或data:URL時(shí),origin屬性的值是加載URL的腳本的原點(diǎn)。

在擴(kuò)展中使用window.postMessage

window.postMessage可用于在chrome代碼中運(yùn)行的JavaScript(例如,在擴(kuò)展和特權(quán)代碼中),但調(diào)度事件的source屬性始終為null,以此作為安全限制。(其他屬性具有預(yù)期值。)

內(nèi)容或Web上下文腳本無法指定targetOrigin直接與擴(kuò)展(后臺(tái)腳本或內(nèi)容腳本)通信。Web或內(nèi)容的腳本可以使用帶有"*"的targetOrigin的window.postMessage與來廣播到每一個(gè)偵聽器,但是不鼓勵(lì)這樣做,因?yàn)閿U(kuò)展不能確定這樣的消息,和其他偵聽器(包括那些你不控制的偵聽器)可以偵聽。

內(nèi)容腳本應(yīng)使用runtime.sendMessage與后臺(tái)腳本進(jìn)行通信。Web上下文腳本可以使用自定義事件與內(nèi)容腳本進(jìn)行通信(如果需要,可以隨機(jī)生成事件名稱,以防止從客戶頁面進(jìn)行窺探)。

最后,將消息發(fā)布到file:URL處的頁面,當(dāng)前要求targetOrigin參數(shù)為"*"。file://不能用作安全限制;此限制可能會(huì)在將來修改。

規(guī)范

規(guī)范 狀態(tài) 注釋
HTML Living Standard 
規(guī)范中'postMessage()'的定義。
Living Standard
 

瀏覽器兼容性

新的兼容性表格處于測(cè)試階段

電腦端 移動(dòng)端
Chrome
Edge
Firefox
Internet Explorer
Opera
Safari
Android webview Chrome for Android
Edge Mobile Firefox for Android
Opera for Android
iOS Safari
基本支持 支持:1 支持
支持:8

支持:10

支持:9.5 支持:4 支持 支持 支持
支持:8

支持 支持
transfer參數(shù) 支持 支持:20 支持 ? ? ? ? 支持:20 ? ?
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)