window.postMessage は、安全にクロスドメイン通信を可能にするためのメソッドです。通常、異なった複数のページでのスクリプトはそれらが実行されたページが同じプロトコル(たいてい http)、ポート番号(http のデフォルトは 80)、ホスト(両方のページによって同じ値に設定される document.domain を基準とする)である場合に限りお互いにアクセスすることだけが可能です。window.postMessage は正しく使われたときに安全な方法でこの制限を回避するための制御された仕組みを提供します。
概要
window.postMessage が呼び出されたとき、MessageEvent を対象ウィンドウに伝達し、そのとき、実行されなければならない任意の保留中のスクリプトが完了します(例えば、window.postMessage がイベントハンドラから呼ばれた場合イベントハンドラの存続、以前に設定された保留中のタイムアウト、など)。 MessageEvent には message という型、window.postMessage に与えられる第一引数の文字列の値に設定される data プロパティ、 window.postMessage が呼び出されたとき、window.postMessage を呼び出しているウィンドウ内のメインドキュメントの生成元に対応する origin プロパティ、window.postMessage を呼び出したウィンドウである source プロパティがあります。(他のイベントの標準プロパティがそれらの期待される値で存在します)
構文
otherWindow.postMessage(message, targetOrigin);
otherWindow- 別ウィンドウへの参照。次のような参照が保持されます。例えば、
iframe要素のcontentWindowプロパティの利用、window.open によって返されるオブジェクト、あるいは、 window.frames における名前付きか数による添え字。 message- 他のウィンドウに送られる文字列データ。
targetOrigin- イベントが伝達されるべき
otherWindowの生成元を"*"というリテラル文字列(指定しないことを示します)か URI のいずれかで指定します。もしイベントがtargetOriginで指定されたものにマッチしないotherWindowの文書のスキーマ、ホスト名、あるいは、ポートに伝達するようになっている場合、そのイベントは伝達されません。つまり、3 つすべてがマッチした場合にだけイベントが伝達されます。この仕組みはメッセージが送られる場所の制御を提供します。例えば、postMessageをパスワードを送るために利用する場合、悪意のある第三者によるパスワードの傍受を防ぐためにこの引数が意図されたパスワードを含むメッセージの受け手と同じ生成元である URI であることは絶対的に致命的なものになるでしょう。
伝達されるイベント
以下のJavaScriptを実行することで、otherWindowに伝達されたメッセージを受け取ることができます。
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
if (event.origin !== "http://example.org:8080")
return;
// ...
}
伝達されたメッセージが持つプロパティには次の3つがあります。
data- 他のウィンドウから渡されるメッセージを保持しているオブジェクト。
originpostMessageが呼び出されたときにメッセージを送るウィンドウの origin。この文字列は、プロトコルと "://"、ホスト名(存在する場合)、そして、":" の後に続くポート番号(デフォルトポートと指定するポートが異なる場合)が連結されたものです。典型的な生成元の例はhttps://example.org(この場合のポートは443)、http://example.net(この場合のポートは80)、そしてhttp://example.com:8080。この生成元はそのウィンドウの現在もしくは将来の生成元であることを保証していないことに注意してください。postMessageが呼び出されてから異なる場所に移動するかもしれません。source- メッセージを送った
windowオブジェクトへの参照。これを使うことで異なった生成元である二つのウィンドウ間で双方向の通信を確立することができます。
セキュリティに関すること
他のサイトからメッセージを受け取りたくない場合、message イベントのための一切のイベントリスナーを追加するべきではありません。これはセキュリティ的な問題を避けるための完全にフールプルーフな方法です。
他のサイトからメッセージを受け取りたい場合、origin あるいは source プロパティを用いて常に送信者の識別情報を確かめてください。任意のウィンドウ(例えば、http://evil.example.com も含む)は任意の他のウィンドウにメッセージを送ることができ、見知らぬ送信者が悪意あるメッセージを送らない保証はありません。識別情報を確かめても、常に受け取ったメッセージの構文を確かめるべきです。さもなければ、信頼されたメッセージだけを送ることが信頼されているサイトにおけるセキュリティホールがクロスサイトスクリプティングセキュリティホールをあなたのサイトに開けることになり得ます。
例
/*
* <http://example.com:8080> にある、window A のスクリプト:
*/
var popup = window.open(...ポップアップの詳細...);
// ポップアップブロッカーでブロックされなかった場合に、ポップアップが完全に
// ロードされたとき
// ウィンドウがその場所を変更していない場合、これは何もしません。
popup.postMessage("ユーザ名は 'bob' 、パスワードは 'secret' です",
"https://secure.example.net");
// ウィンドウがその場所を変更していない場合、これはポップアップに送るメッセ
// ージを首尾よくキューします。
popup.postMessage("hello there!", "http://example.org");
function receiveMessage(event) {
// このメッセージの送信者は信頼している者か?(例えば、最初開いたものと違
// うかもしれません)。
if (event.origin !== "http://example.org")
return;
// event.source は popup
// event.data は "hi there yourself! the secret response is: rheeeeet!"
}
window.addEventListener("message", receiveMessage, false);
/*
* <http://example.org> で実行される popup のスクリプト:
*/
// postMessage が呼び出された後に呼び出されます。
function receiveMessage(event) {
// このメッセージの送信者は信頼している者か?
if (event.origin !== "http://example.com:8080") {
return;
}
// event.source は window.opener
// event.data は "hello there!"
// 受け取ったメッセージの生成元を確かめた場合(どんな場合でもそうするべ
// きです)、メッセージに返答するための便利なイディオムは event.source 上
// の postMessage を呼び出し、targetOrigin に event.origin を指定すること
// です。
event.source.postMessage(
"hi there yourself! the secret response " +
"is: rheeeeet!",
event.origin
);
}
window.addEventListener("message", receiveMessage, false);
注記
任意のウィンドウが、いつでも、ウィンドウの文書の場所にかかわらず、メッセージを送るために、任意の他のウィンドウ上でこのメソッドにアクセスするかもしれません。従って、任意のイベントリスナーはメッセージを受け取る際に、origin あるいは source プロパティを用いて、まず最初にメッセージの送信者の識別情報をチェックしなければなりません。これを軽視することはできません。なぜなら、origin あるいは source プロパティのチェックの失敗はクロスサイトスクリプティング攻撃を可能にするからです。
非同期で伝達されるスクリプト(タイムアウト、ユーザが生成したイベント)のために postMessage の呼び出し元の判別が不可能であるとき、postMessage によって送られるイベントを待ち受けているイベントハンドラは例外を投げます。
伝達されるイベントの origin プロパティは呼び出すウィンドウの document.domain の現在の値に影響されません。
IDN ホスト名に限った話ですが、origin プロパティの値は一貫して Unicode もしくは Punycode ではありません。ですから、IDN サイトからのメッセージを期待する場合にこのプロパティを用いるときは、最も素晴らしい互換性チェックにするために、IDN と Punycode の値の両方をチェックしてください。この値は最終的には 一貫して IDN になるはずですが、現在は IDN と Punycode 両方の形式を扱うべきです。
javascript: や data: を含む送信ウィンドウの origin プロパティの値は URL を読み込んだスクリプトの生成元になります。
window.postMessage は chrome コードで実行される JavaScript で利用可能です(例:拡張内及び特権コード)。しかし、伝達されるイベントの source プロパティはセキュリティ上の制限から常に null です(他のプロパティは期待された値です)。現在、chrome: URL のウィンドウに送られるメッセージに対する targetOrigin 引数は "*" だけであると誤って解釈されます。この値はターゲットウィンドウが悪意あるサイトによって別の場所に誘導され得るという危険なものですから、現在は postMessage を chrome:ページと通信するために用いないことを推奨します。chrome ウィンドウと通信するには(ウィンドウが開かれたときのクエリ文字列のような)別の方法を用いてください。最後に、file: URL のページへのメッセージを送るには targetOrigin 引数を "*" にする必要があります。file:// はセキュリティ上の制限のために用いることはできません、この制限は将来修正されるかもしれません。
仕様
window.postMessage は draft HTML5 specification で定義されています。