CORS & Same Origin Policy 入門
CORS(オリジン間リソース共有) は Web セキュリティの観点において、Web 開発・Web 制作に関わる方すべてが理解しておくべき仕組みです。
本記事では、この CORS についての概要と一般的な設定方法について解説していきます。
CORS とは
CORS(Cross-Origin Resource Sharing, オリジン間リソース共有)は、自身のオリジンから見た、別のオリジン(クロスオリジン)からのリクエストを許可するか決定する仕組みです。(特定条件下で、通常は後述する Same Origin-Policy の機構により、ブロックされます)
CORS の概要
Web ページの HTML には、画像やビデオ、CSS、JavaScript、iframe などを HTML タグを使って自由に埋め込むことができます。
ただし、別ドメインへ向けた一部の非同期的なリクエスト(正確にはクロスオリジンのリソース使用)については「同一オリジンポリシー」と呼ばれる仕組みにより、デフォルトではアクセスできないようブロックされています。
CORS ポリシーによりリクエストがブロックされた例(Chrome DevTools)
この仕組みにより「XSS(クロスサイトスクリプティング)」の脆弱性については、問題が他のオリジンに波及しないようになっており、また「CSRF(クロスサイトリクエストフォージェリ)」の脆弱性についても、非同期リクエストを利用したケースにおいては、容易に起きないようになっているともいえるでしょう。
オリジンとはなにか
CORS を理解するうえで必要なのが「オリジン」の概念です。
オリジンは「スキーム + ホスト + ポート番号」の組み合わせをいいます。
ドメインと似ていますが、プロトコルとポート番号を含んでいる点がドメインとは異なります。
2 つのオリジンの「スキーム + ホスト + ポート番号」が一致している場合、両者は「同一のオリジンである」とみなされます。
オリジンの定義
Same Origin Policy(同一オリジンポリシー)とはなにか
Same Origin Policy(同一オリジンポリシー、同一生成元ポリシー)は、あるオリジンで読み込まれたリソース(HTML, スクリプト, Cookie など)が、異なるオリジン(クロスオリジンとも呼ばれる)のリソースとの通信・アクセスを制限する、Web ブラウザが持つセキュリティ機能です。
異なるオリジンからロードされたリソースへのアクセスをブロックすることにより、元オリジンで読み込まれたリソースに対して悪い影響を及ぼさないようにすることを目的としています。
※ 本記事では、以後 Same Origin Policy を「同一オリジンポリシー」で説明します。
Same Origin Policy(同一オリジンポリシー)が適用されるケース、適用されないケース
同一オリジンポリシーは、設計当初は JavaScript による Ajax や Web フォントなど、ネットワーク通信に関わる部分のみ適用されていましたが、その後の拡張でローカルファイルやブラウザ特権のリソースなど、幅広い範囲に適用されるようになりました。(誤解が発生しやすくなるため、本記事では主にネットワーク通信における同一オリジンポリシーについてのみ記載します)
同一オリジンポリシーが適用されるケース
以下のように、非同期でリソースを読み込もうしている場合は、同一オリジンポリシーによる制約が適用されます。
- JavaScript での非同期通信(Ajax, いわゆる XMLHttpRequest や Fetch API を使った通信)
- CSS での @font-face を使った Web フォントの読み込み
- WebGL テクスチャの読み込み
- canvas の drawImage() を用いた画像やビデオフレームの描画
- 画像から CSS シェイプの生成
これらは通常、後述する「シンプルリクエスト」では無いケースであり、同一オリジンポリシーによって制限が発生するリクエストです。
同一オリジンポリシーが適用されないケース
- <img>タグのsrc属性で読み込んだ画像
- <link>タグのhref属性で読み込んだ CSS
- <script>タグのsrc属性で読み込んだ JavaScript
- <form>タグの action 属性で設定した送信先 URL
- <video>, <audio> タグのsrc属性で読み込んだマルチメディアファイル
-
<iframe>, <frame> タグのsrc属性での別サイトコンテンツの読み込み
- X-Frame-Options の設定によっては読み込みがブロックされます
- JavaScript を用いて iframe 内のドキュメントにアクセスすることはできません
異なるオリジンへアクセスできるように CORS を設定する
前項でご紹介した「同一オリジンポリシー」という制約により、非同期では別オリジンにあるリソースにはアクセスできないことがわかりました。
ただ、Web 開発・制作を行うにあたり、異なるオリジンにあるリソースへアクセスしたいケースもあるかと思います。
たとえば、Web サービスがサブドメインごとにコンテンツが分かれていて、別のサブドメインのコンテンツにアクセスしたいケースなどです。
こういったケースにおいて、CORS(オリジン間リソース共有)を設定することにより、同一オリジンポリシーの制約を回避・緩和することができます。
同一オリジンポリシーと CORS という文脈の中では、「シンプルリクエスト」「プリフライトリクエスト」という 2 種類のリクエストが登場します。
同一オリジンポリシーによって制限が発生するかは、通常「シンプルリクエスト」であるかどうかで決まります。シンプルリクエストでなかった場合、ブラウザのセキュリティチェックフローに則って、プレフライトリクエストを交えたリクエストフローが実施されます。
シンプルリクエスト / プリフライトリクエスト非同期通信のフローチャート(Wikipedia より引用)
シンプルリクエストであるかどうかは、次の表の条件に当てはまるかで決まります。
シンプルリクエストのケース |
以下の条件すべてに合致する
|
---|---|
プリフライトリクエストが発生するケース |
以下の条件のいずれかに合致する
|
一例として「api.example.com 上の Web アプリケーションにおいて、www..example.com からのリクエストを許可したい」という前提のもと、代表的な設定方法をご紹介します。
シンプルリクエスト
シンプルリクエストの場合、特別何かを実施しなければ外部オリジンへの通信が行えないといったことはありません。
プリフライトリクエスト
プリフライトリクエストは、実リクエストを行う前に OPTIONS メソッドを用いて別オリジンのサーバと通信を行い、許可される通信元であるかどうかを事前に確認する HTTP リクエストであり、Web ブラウザが自動的にプリフライトリクエストを行います。
プリフライトリクエストのレスポンスが確認できた後に、実際のリクエストが送信されるようになっています。
また、プリフライトリクエストのレスポンスが許可されていないことを示していた場合は、実際のリクエストは送信されず、リクエストが終了します。
ヘッダーを追加する
クライアントサイドでの設定
クライアントサイド(Web ブラウザ → Web サーバ)では、リクエストヘッダーに以下が追加されます。
基本的にはブラウザが自動で付与するため、特段何かを実施する必要はありません。
しかし Fetch APIを用いてリクエストを送信する場合は、オプションの設定が必要です。
サーバサイドでの設定
サーバサイドでは、以下をレスポンスヘッダーを付加します。
Access-Control-Allow-Origin レスポンスヘッダーを付与することにより、オリジンを越えたアクセスを許可することを Web ブラウザに知らせることができます。
また Access-Control-Allow-Methods によって、クロスオリジンでの通信で許可されるリクエストメソッドを定義し、ブラウザに知らせることができます。
最低限上記二つを実施すればクロスオリジン間の通信が行えます。
異なるオリジンへの通信で Cookie や Basic 認証を利用したい場合
CORS が適用されるリクエストにおいて、デフォルトでは Cookie や Basic 認証に関わる情報は送信されません。
クロスオリジンの Ajax リクエストにおいて、Cookie の送信、Basic 認証といったクレデンシャル情報(Creadentials)を付加したい場合、クライアントサイド(JavaScript)側でwithCredentialsと呼ばれるオプションをあらかじめ JavaScript 側で付与しておく必要があります。
以下は代表的なライブラリである jQuery と axios および XMLHttpRequest での設定例です。
jQuery の場合
axios の場合
XHLHttpRequest (XHR) の場合
また、Cookie 操作を行う場合、バックエンドの当該レスポンスヘッダーに Access-Control-Allow-Credentials: true を付加する必要になります。
※Credentials を必要とするリクエストの場合、Access-Control-Allow-Origin で許可するオリジンを明示的に指定する必要がありますのでご注意ください。( * は使えません。)
また、クロスオリジンのサーバーによってセットされた Cookie は、クライアント側の JavaScript からはアクセスできません。
あくまでクロスオリジンのサーバで Cookie が必要な場合にのみ利用するようにしましょう。
CORS の設定を支援するライブラリ
前項では CORS で具体的に追加する HTTP ヘッダーについてご紹介しましたが、代表的なフレームワークにおいてヘッダーの追加を支援するライブラリもありますので、参考までにご紹介します。
フレームワーク(言語) |
CORS 設定を支援するライブラリ |
---|---|
Ruby on Rails (Ruby) |
|
Laravel (PHP) |
|
Django (Python) |
|
Spring (Java, Kotlin) |
標準で CORS サポートあり |
Play Framework (Scala, Java) |
標準で CORS サポートあり |
さいごに
CORS および同一オリジンポリシーは、Web ブラウザにおける Web セキュリティの肝となるものです。
ただし、CORS はあくまで同一オリジンポリシーの制約の中で別のオリジンへアクセスを行うための仕組みであり、セキュリティの問題を本質的に解決するものではありません。
CORS が設定できていたとしても「XSS(クロスサイトスクリプティング)」や「CSRF(クロスサイトリクエストフォージェリ)」が発生するリスクはあります。
Web 開発者・制作者・セキュリティ担当者は今一度、CORS および同一オリジンポリシーについて理解し、適切な脆弱性対策と実装を行うよう、心がけていきましょう。
最後になりますが、Web アプリケーションで用いられるフレームワーク・ライブラリに潜む脆弱性については、yamory を用いることで検出が可能です。
ぜひ一度お試しいただければ幸いです。