實現兩個視窗通訊方法之postMessage

itKingOne發表於2018-07-18

兩個視窗的通訊問題應該是一個比較常見的問題了,通訊可以通過很多種方式,如下:

localStorage 
postMessage 
websocket

今天博主先來一篇postMessage乾貨,後期還會給出websocket的實現程式碼。 
首先來看一下基本的語法

otherWindow.postMessage(message, targetOrigin, [transfer]);

  1. otherWindow

    其他視窗的一個引用,比如iframe的contentWindow屬性、執行window.open返回的視窗物件、或者是命名過或數值索引的window.frames。

  2. message

    將要傳送到其他 window的資料。它將會被結構化克隆演算法序列化。這意味著你可以不受什麼限制的將資料物件安全的傳送給目標視窗而無需自己序列化。

  3. targetOrigin

    通過視窗的origin屬性來指定哪些視窗能接收到訊息事件,其值可以是字串”“(表示無限制)或者一個URI。在傳送訊息的時候,如果目標視窗的協議、主機地址或埠這三者的任意一項不匹配targetOrigin提供的值,那麼訊息就不會被髮送;只有三者完全匹配,訊息才會被髮送。這個機制用來控制訊息可以傳送到哪些視窗;例如,當用postMessage傳送密碼時,這個引數就顯得尤為重要,必須保證它的值與這條包含密碼的資訊的預期接受者的orign屬性完全一致,來防止密碼被惡意的第三方截獲。如果你明確的知道訊息應該傳送到哪個視窗,那麼請始終提供一個有確切值的targetOrigin,而不是。不提供確切的目標將導致資料洩露到任何對資料感興趣的惡意站點。

  4. transfer

    是一串和message 同時傳遞的 Transferable 物件. 這些物件的所有權將被轉移給訊息的接收方,而傳送一方將不再保有所有權。


message的屬性:

  1. data

    從其他 window 中傳遞過來的物件。

  2. origin

    呼叫 postMessage 時訊息傳送方視窗的 origin . 這個字串由 協議、“://“、域名、“ : 埠號”拼接而成。例如 “https://example.org (implying port 443)”、“http://example.net (implying port 80)”、“http://example.com:8080”。請注意,這個origin不能保證是該視窗的當前或未來origin,因為postMessage被呼叫後可能被導航到不同的位置。

  3. source

    對傳送訊息的視窗物件的引用; 您可以使用此來在具有不同origin的兩個視窗之間建立雙向通訊。


安全方面 
如果不接受message,那麼就不要新增message事件的監聽器

如果您確實希望從其他網站接收message,請始終使用originsource屬性驗證發件人的身份。 任何視窗(包括例如http://evil.example.com)都可以向任何其他視窗傳送訊息,並且您不能保證未知發件人不會傳送惡意訊息。 但是,驗證身份後,您仍然應該始終驗證接收到的訊息的語法。 否則,您信任只傳送受信任郵件的網站中的安全漏洞可能會在您的網站中開啟跨網站指令碼漏洞。

當您使用postMessage將資料傳送到其他視窗時,始終指定精確的目標origin,而不是*。 惡意網站可以在您不知情的情況下更改視窗的位置,因此它可以攔截使用postMessage傳送的資料。

完整示例如下:

//a.com/index.html
<iframe src="b.com/index.html" id="ifr">
</iframe>
<script>
window.onload = function(){
  var iframe = document.getElementById('ifr');
  var targetOrigin = 'http://b.com'; // 若寫成'http://b.com/c/proxy.html'效果一樣
                                        // 若寫成'http://c.com'就不會執行postMessage了
  iframe.contentWindow.postMessage('data to send',targetOrigin);
}
</script>



// b.com/index.html
<script type="text/javascript">
  window.addEventListener('message',function(event){
    // 通過origin屬性判斷訊息來源地址
    if(event.origin == 'http://a.com'){
      console.log(event.data);
      console.log(event.source);
    }
  },false);
</script>

相關文章