サイト脆弱性をチェックしよう! -- 第19回:CORS(Cross-Origin Resource Sharing)
<目次>
目次[非表示]
- 1.「オリジン」と「同一オリジンポリシー」について
- 1.0.1.オリジンとは
- 1.0.2.同一オリジンポリシーとは
- 1.0.3.異なるオリジンにあるリソースにアクセスしたいという要望の増加
- 2.CORSについて
- 3.二種類のリクエスト
- 3.0.1.「シンプルなリクエスト」
- 3.0.2.「プリフライトリクエスト」
- 4.CORS の仕組み
- 4.0.1.「シンプルなリクエスト」
- 4.0.2.「プリフライトリクエスト」
- 4.0.3.クライアントサイドでの設定
- 4.0.4.サーバサイドでの設定
- 4.0.5.Cookie や Basic 認証を利用したい場合の設定
- 4.0.6.クライアントサイドでの実装例
- 5.まとめ
「オリジン」と「同一オリジンポリシー」について
まず、CORSを理解するのに必要な「Origin(オリジン)」と「Same-Origin Policy(同一オリジンポリシー)」について説明する。
オリジンと同一オリジンポリシーは、JavaScriptの開発以来、長らく概念として存在はしていたが、2011年の「RFC6454 - The Web Origin Concept」で正式に仕様書として記述された仕組み及び考え方である。
オリジンとは
具体的には「RFC6454」に記述されているが、オリジンとは、スキーム、ホスト、ポートの3つの組み合わせのことを指す。
下記のURLを例とするなら、スキームが「https」、ホストが「example.com」、ポートが「443」となり、どれか1つでも異なる場合は異なるオリジンとして判断される。
同一オリジンポリシーとは
ユーザのセキュリティ強化を目的として、ウェブブラウザに実装されたリソースの取り扱いに関する仕組みである。
スキーム、ホスト、ポートの組み合わせが同じ場合は同一のオリジンとみなし、異なるオリジン(クロスオリジン)からのリソースへのアクセスを制限する。
異なるオリジンにあるリソースにアクセスしたいという要望の増加
近年Ajaxを代表とするWebの発達により、同一オリジンポリシーを回避した実装が求められるようになったため、JSONPという技術が流行した。
ここではJSONPについての詳しい解説は省略するが、異なるオリジンにあるデータにアクセスし情報を取得するという仕組みを悪用したセキュリティの問題があったため、安全に回避するための方法として新たに「CORS」という仕組みが考案された。
CORSについて
CORS(Cross-Origin Resource Sharing)とは、あるオリジンで動いているWeb アプリケーションに対して、異なるオリジンとのデータ交換を可能にするセキュリティ上の仕組みであり、CORSを利用することで、オリジン間の安全なリクエストやブラウザ、サーバ間のデータ転送を行うことができる。
CORSはいくつかの実装で用いられるが、今回はXMLHttpRequestのケースを例として説明する。
二種類のリクエスト
CORS では「シンプルなリクエスト」の場合と、「プリフライトリクエスト」の場合で仕組みが変わる。それぞれの条件は以下の通りとなる。
「シンプルなリクエスト」
「シンプルなリクエスト」とは、以下の条件をすべて満たすリクエストである。
・メソッドは下記のうちいずれか
GET
POST
HEAD
・XMLHttpRequestオブジェクトのsetRequestHeaderメソッドで設定するリクエストヘッダが以下のみである場合
Accept
Accept-Language
Content-Language
Content-Type
・Content-Typeヘッダは以下のいずれか
application/x-www-form-urlencoded
multipart/form-data
text/plain
「プリフライトリクエスト」
「シンプルなリクエスト」の条件を満たさない場合、「プリフライトリクエスト」として扱われる
・メソッドが「GET」「POST」「HEAD」以外
・Content-Typeヘッダが「application/x-www-form-urlencoded」「multipart/form-data」「text/plain」のいずれでもない場合
・XMLHttpRequestオブジェクトのsetRequestHeaderメソッドで設定するリクエストヘッダが、シンプルリクエストで許可されたリクエスト以外にも付与されている場合
CORS の仕組み
今回は「example.com」上のHTMLから「api.example.net」上のコンテンツを呼び出すという想定で説明する。
「シンプルなリクエスト」
異なるオリジンにHTTPリクエストを送信する場合は、同一オリジンポリシーの制約を解除するためにOrigin や Access-Control-Allow-Origin ヘッダを使用する。
①利用者がWebサイト「https://example.com」にアクセスする。
②「api.example.net」へのリクエストを送信するコードを含むレスポンスを返す。
③アクセスしたページは別ドメイン「api.example.net」のコンテンツを含むため、別ドメインへ呼び出しを行う。異なるオリジンへのリクエストを行う場合、ブラウザでは自動的にHTTPリクエストに「Origin」ヘッダを追加する。
④「Origin」ヘッダを参照し、example.netからのアクセス許可があることを確認し、レスポンスデータをユーザに渡す。許可が確認できなければ、エラーとする。
⑤ ブラウザが「Access-Control-Allow-Origin」ヘッダの内容を確認し、「example.com」が含まれていた場合は、「example.com」内で「api.example.net」のコンテンツを利用できるようにする。
「プリフライトリクエスト」
「シンプルなリクエスト」ではない場合は、ブラウザは、事前にサーバに自動的に問い合わせ、 アクセスが許可されるかどうか確認する。プリフライトリクエストのレスポンスが確認できた後に、実際のリクエストが送信される仕組みとなっている。
クライアントサイドでの設定
事前の問い合わせでは、これから使う HTTP メソッドや HTTP ヘッダの種類を申告することが必要となる。
この時に Originヘッダ、 Access-Control-Request-Method ヘッダ、 Access-Control-Request-Headers ヘッダが必要となるが、こちらはブラウザが以下のようにリクエストヘッダを自動で追加するため、特別な設定を行う必要はない。
サーバサイドでの設定
サーバサイドではこれらをチェックしてアクセスを許可する場合、Access-Control-Allow-Origin ヘッダ、Access-Control-Allow-Methodヘッダ、Access-Control-Allow-Headersヘッダを付加させる必要がある。
上記のサーバからのアクセス許可が確認できてから、実際の HTTP 要求が送信される。
Cookie や Basic 認証を利用したい場合の設定
クロスオリジンへの通信では、デフォルトでは Cookie や 認証に関する情報はリクエストヘッダに自動的に送信されない。
異なるオリジンに Cookie を送りたい場合、XMLHttpRequestのwithCredentialsプロパティをtrueにセットする必要がある。
クライアントサイドでの実装例
上記に対して、レスポンスヘッダとしてAccess-Control-Allow-Credentials: true を返すようにする必要がある。
また、注意点としてwithCredentials を設定する場合は、Access-Control-Allow-Origin では “*” を指定するとエラーとなる。許可するオリジンを明示的に指定する必要があるため、注意しなければならない。
まとめ
今回はCORSという仕組みを解説した。
CORS は異なるオリジンへアクセスを行うことを許可する便利な仕組みであるものの、セキュリティの問題は解決できない。このため、脆弱性対策や適切な実装を併せて行う必要がある。