W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
該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ì)象暴露給接收窗口。
targetWindow .postMessage(message,targetOrigin,[ transfer ]);
targetWindow
Window.open
(生成一個(gè)新窗口然后引用它),Window.opener
(引用產(chǎn)生這個(gè)的窗口),HTMLIFrameElement.contentWindow
(<iframe>
從其父窗口引用嵌入式),Window.parent
(從嵌入式內(nèi)部引用父窗口<iframe>
)Window.frames
+索引值(命名或數(shù)字)。message
targetOrigin
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
(可選的)otherWindow可以通過執(zhí)行以下JavaScript來偵聽已分派的消息:
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
if (event.origin !== "http://example.org:8080")
return;
// ...
}
調(diào)度消息的屬性是:
data
origin
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
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ù)。
/*
* 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)。
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ī)范 | 狀態(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 | ? | ? |
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: