同一オリジンポリシー
同一オリジンポリシーは重要なセキュリティの仕組みであり、あるオリジンによって読み込まれた文書やスクリプトが、他のオリジンにあるリソースにアクセスできる方法を制限するものです。
これにより、悪意のある可能性のあるドキュメントを隔離し、起こりうる攻撃のベクターを減らすことができます。例えば、インターネット上の悪意のあるウェブサイトがブラウザー内で JS を実行して、 (ユーザーがサインインしている) サードパーティのウェブメールサービスや (公開 IP アドレスを持たないことで攻撃者の直接アクセスから保護されている) 企業のイントラネットからデータを読み取り、そのデータを攻撃者に中継することを防ぎます。
オリジンの定義
二つのページのプロトコル、ポート番号 (もしあれば)、ホストが等しい場合、両者のページは同じオリジンです。これは「スキーム/ホスト/ポート番号のタプル」または時に単に「タプル」として参照されます (「タプル」は共に全体を構成する三つの部分の組み合わせを表します)。
以下の表は、各行の URL が http://store.company.com/dir/page.html と同じオリジンであるかを比較したものです。
| URL | 結果 | 理由 |
|---|---|---|
http://store.company.com/dir2/other.html |
同一オリジン | パスだけが異なる |
http://store.company.com/dir/inner/another.html |
同一オリジン | パスだけが異なる |
https://store.company.com/page.html |
不一致 | プロトコルが異なる |
http://store.company.com:81/dir/page.html |
不一致 | ポート番号が異なる (http:// は既定で 80 番ポート) |
http://news.company.com/dir/page.html |
不一致 | ホストが異なる |
オリジンの継承
about:blank や javascript: の URL のページから実行されたスクリプトは、その URL にオリジンのサーバーについての情報が明示的に含まれていないため、その URL を開いた文書のオリジンを継承します。
メモ: 例えば、 about:blank は (例えば Window.open() メカニズムを使用して) 新しい空のポップアップウィンドウを生成し、その中に親スクリプトがコンテンツを書き込むために使用されます。ポップアップウィンドウにもコードが含まれた場合、そのコードはそれを生成したスクリプトと同じオリジンを継承します。
警告: data: の URL は新しく、空のセキュリティコンテキストを生成します。
IE における例外事項
Internet Explorer では、同一オリジンポリシーについて二つの大きな例外があります。
- 信頼済みゾーン
-
双方のドメインが高く信頼されたゾーン (企業のドメインなど) である場合は、同一オリジンの制限が適用されません。
- ポート番号
-
IE は同一オリジンの確認要素にポート番号を含みません。従って、
http://company.com:81/index.htmlとhttp://company.com/index.htmlは同一オリジンとみなされ、制限は適用されません。
これらの例外事項は標準外であり、他のブラウザーはこのような挙動に対応していません。
オリジンの変更
警告: ここで説明している (document.domain セッターを使用する) 方法は非推奨です。同一オリジンポリシーによるセキュリティ保護を弱め、ブラウザーのオリジンモデルを複雑にして、相互運用性の問題やセキュリティバグを引き起こすためです。
ページのオリジンは、いくつかの制限の下で変更されることがあります。スクリプトを用いると、 document.domain の値を現在のドメインまたは上位ドメインに変更できます。スクリプトによって現在のドメインの上位ドメインへオリジンが変更された場合、より短くなったドメイン名は次回のオリジン検査時に用いられます。
例えば、 http://store.company.com/dir/other.html にあるドキュメント内のスクリプトが以下のコードを実行したと仮定します。
js
document.domain = "company.com";
このコードが実行された後、そのページは http://company.com/dir/page.html におけるオリジンの検査を通過できます (許可を明示するために http://company.com/dir/page.html が自身の document.domain を "company.com" に変更したと仮定します。詳しくは document.domain を参照してください)。しかし、 company.com が自身の document.domain を othercompany.com に変更することはできません。なぜなら company.com の上位ドメインではないためです。
ブラウザーはポート番号を個別に検査します。 document.domain を呼び出すと、 document.domain = document.domain の場合も含め、ポート番号が null で上書きされます。従って、スクリプトの最初に document.domain = "company.com" を設定しただけでは、 company.com:8080 と company.com とは互いにアクセスできません。双方のポートが null になるように、双方で設定しなければなりません。
この仕組みにはいくつかの制限があります。例えば、 document-domain の Feature-Policy が有効になっている場合や、文書がサンドボックス化された <iframe> 内にある場合は、 "SecurityError" の DOMException が発生します。また、この方法でオリジンを変更しても、多くの Web API (localStorage, indexedDB, BroadcastChannel, SharedWorker など) で使用されているオリジンチェックには影響しません。失敗事例のより詳細なリストは、 Document.domain > Failures にあります。
メモ: サブドメインから親ドメインへアクセスさせるために document.domain を使用する際は、親ドメインとサブドメインの双方で同じ値を document.domain に設定することが必要です。この作業は、親ドメインを元の値に戻す際にも必要です。これを怠ると権限エラーが発生します。
異なるオリジンへのネットワークアクセス
XMLHttpRequest や <img> 要素を使用する場合など、 同一オリジンポリシーは 2 つのオリジン間における通信を制御します。一般にこれらの通信は 3 つのカテゴリに分類されます。
- 異なるオリジンへの書き込みは、概して許可されます。例えばリンクやリダイレクト、フォームの送信などがあります。まれに使用される HTTP リクエストの際はプリフライトが必要です。
- 異なるオリジンの埋め込みは、概して許可されます。例は後述します。
- 異なるオリジンからの読み込みは一般に許可されませんが、埋め込みによって読み取り権限がしばしば漏れてしまいます。例えば埋め込み画像の幅や高さ、埋め込みスクリプトの動作内容、あるいは埋め込みリソースでアクセス可能なものを読み取ることができます。
以下に挙げるのは、異なるオリジンに埋め込むことができるリソースの例です。
- JavaScript を
<script src="..."></script>で使用する場合。構文に関するエラーメッセージは、同一オリジンのスクリプトについてのみ読み取り可能です。 - CSS を
<link rel="stylesheet" href="...">で使用する場合。 CSS は緩い構文規則を持っているため、オリジンをまたぐ CSS には適切なContent-Typeヘッダーを付加することが必要です。制約はブラウザーにより異なり、ブラウザーごとの詳細は Internet Explorer, Firefox, Chrome, Safari (日本語訳) (CVE-2010-0051 までスクロールしてください), Opera の各項目を参照してください。 <img>で表示された画像。<video>および<audio>で再生されたメディア。<object>または<embed>で埋め込まれた外部リソース。@font-faceが適用されたフォント。異なるオリジンのフォントを許容するブラウザーもありますが、同一オリジンを要求するものもあります。<iframe>に関連するあらゆること。このような形のオリジン間のやりとりを防ぐため、サイトにX-Frame-Optionsヘッダーを使用することができます。
異なるオリジンへのアクセスを許可する方法
異なるオリジンへのアクセスをブロックする方法
- 異なるオリジンへの書き込みを防ぐには、リクエスト内の Cross-Site Request Forgery (CSRF) トークンと呼ばれる推測できないトークンをチェックしてください。このトークンを知っているページのオリジンをまたがった読み込みを防ぎます。
- 異なるオリジンからのリソースの読み込みを防ぐには、そのリソースが埋め込まれないようにします。リソースの埋め込まれると情報が漏えいする場合があるため、多くの場合は埋め込みの抑止が必要になります。
- 異なるオリジンによる埋め込みを防ぐには、リソースの形式が先ほど述べたような埋め込み可能な形式だと思われないようにします。ほとんどの場合、ブラウザーは
Content-Typeを尊重しません。例えば<script>タグで HTML 文書を指した場合、ブラウザーは HTML を JavaScript としてパースしようとします。リソースがサイトの入口ではない場合は、埋め込みを抑止するため CSRF トークンも使用するとよいでしょう。
異なるオリジンへのスクリプトからの API によるアクセス
iframe.contentWindow, window.parent, window.open, window.opener といった JavaScript API を用いると、ドキュメントが直接互いに参照することができます。2 つのドキュメントが同一のオリジンではない場合、 Window オブジェクトや Location オブジェクトなど、限られたオブジェクトにのみアクセスすることができます。詳しくは次の 2 つのセクションで説明します。
window.postMessage を使用すると、異なるオリジンの文書間における通信がさらに可能となります。
Window
以下に示した Window のプロパティは、異なるオリジンからのアクセスが許可されています。
| 属性 | |
|---|---|
window.closed |
読み取り専用 |
window.frames |
読み取り専用 |
window.length |
読み取り専用 |
window.location |
読み取り/書き込み |
window.opener |
読み取り専用 |
window.parent |
読み取り専用 |
window.self |
読み取り専用 |
window.top |
読み取り専用 |
window.window |
読み取り専用 |
一部のブラウザーでは、仕様書で定められたものより多くのプロパティでアクセスが許可されています。
Location
以下に示した Location のプロパティは、異なるオリジンからのアクセスが許可されています。
| メソッド |
|---|
location.replace |
| 属性 | |
|---|---|
URLUtils.href |
書き込みのみ |
一部のブラウザーでは、仕様書で定められたものより多くのプロパティでアクセスが許可されています。
オリジンをまたいだデータストレージアクセス
ウェブストレージや IndexedDB など、ブラウザー内部に保存されるデータへのアクセスは、オリジンによって権限が分かれています。それぞれのオリジンが個別にストレージを持ち、あるオリジンの JavaScript から別のオリジンに属するストレージを読み書きすることはできません。
Cookie におけるオリジンの定義は異なります。ページは自身のドメインまたは任意の親ドメイン (親ドメインが public suffix ではない場合に限る) 用の Cookie を設定できます。 ドメインが public suffix であるかを判断する際、Firefox と Chrome は Public Suffix List を使用します。 Internet Explorer は独自の方法で public suffix であるかを判断します。使用しているスキーム (HTTP/HTTPS) やポートに関係なく、ブラウザーはサブドメインも含めて Cookie を使用可能にします。Cookie の設定時に Domain, Path, Secure, HttpOnly の各フラグを用いることで、その Cookie の利用範囲を制限できます。Cookie を読み取るとき、Cookie を設定した場所から知ることはできません。安全な https 接続のみ使用していたとしても、参照している Cookie は安全でない接続を通じて設定された可能性があります。
関連情報
原典情報
- 著者: Jesse Ruderman