HTTP アクセス制御 (CORS)

あるリソースが、始めに自身を提供したドメインとは異なるドメインのリソースを要求するとき、そのリソースはクロスオリジン HTTP リクエストを行います。例えば http://domain-a.com から読み込まれた HTML ページが、http://domain-b.com/image.jpg に対して <img> src でリクエストを行う場合です。今日の Web 上では、多くのページが CSS スタイルシートや画像、スクリプトといったリソースを別のドメインから読み込んでいます。

セキュリティ上の理由からブラウザは、スクリプトによって開始されるクロスオリジン HTTP リクエストを制限します。例えば、XMLHttpRequest同一オリジンポリシー (same-origin policy)に従います。よって、XMLHttpRequest を使用する Web アプリケーションは自身のドメインに限り HTTP リクエストを実行できます。Web アプリケーションを改良するため、開発者はブラウザベンダーに XMLHttpRequest のクロスドメインリクエストを許可するよう求めました。

W3CWeb Applications Working Group が、新たに Cross-Origin Resource Sharing (CORS) 勧告を提示しました。CORS は、Web サーバーがドメインをまたぐアクセスを制御する方法を規定することで、ドメイン間の安全な通信を保証するというものです。CORS を使用する現行のブラウザは、クロスオリジン HTTP リクエストの危険性を緩和するために XMLHttpRequest のような API コンテナ 内で CORS を使用します。

この記事の対象者は Web 管理者、サーバー開発者、フロントエンド開発者です。現行のブラウザはヘッダやポリシーの強制を含む、クロスオリジン共有のクライアント側コンポーネントを扱います。しかしこの新しい標準仕様は、サーバーが新たなリクエストヘッダやレスポンスヘッダを扱わなければならないことを意味します。サーバー開発者向けの補足記事として、サーバーの観点から見たクロスオリジン共有 (PHP コードスニペットを含む) を説明しています。

Cross-Origin Sharing 標準仕様は、以下のような クロスサイト HTTP リクエストを可能にするために使用します:

この記事では Cross-Origin Resource Sharing の全般的な説明と合わせて、Firefox 3.5 で実装した HTTP ヘッダについても説明します。

概要

Cross-Origin Resource Sharing 標準仕様は、Web ブラウザを使用して情報を読み取ることが許可された生成元のセットをサーバが示すことを可能にする、新たな HTTP ヘッダを追加することによって動作します。加えて、ユーザー情報において副作用を引き起こすことがある HTTP リクエストメソッド (特に GET 以外の HTTP メソッド、あるいは特定の MIME タイプを伴う POST) のために、仕様書ではブラウザが HTTP の OPTIONS リクエストメソッドを使用してサーバーに対してサポートしているメソッド要求する "プリフライト" を行って、サーバーの "認可" に基づき実際のリクエストを実際の HTTP メソッドにより送信することを認めています。サーバーは、"クレデンシャル" (Cookie や HTTP 認証データを含む) をリクエストとともに送信するべきかをクライアントに通知することもできます。

以降の章ではシナリオの説明に加え、HTTP ヘッダも詳しく説明します。

アクセス制御シナリオの例

ここでは、Cross-Origin Resource Sharing がどのように動作するかを説明する 3 つのシナリオを提示します。これらの例はすべて XMLHttpRequest オブジェクトを用いており、アクセス制御をサポートするブラウザでクロスサイトのアクセスを行うために使用できます。

本章にある JavaScript の例示コード (およびクロスサイトリクエストを適切に処理するサーバー側コードの実例) は こちらの "in action" にあり、クロスサイトの XMLHttpRequest をサポートするブラウザで動作するでしょう。サーバー側から見た Cross-Origin Resource Sharing の説明 (PHP の例示コードを含む) は こちらにあります

シンプルなリクエスト

シンプルなクロスサイトリクエストとは、以下のすべての条件に合うものです:

  • 許可されたメソッドは以下に限る:
    • GET
    • HEAD
    • POST
  • ユーザエージェントが自動的に設定するヘッダ (ConnectionUser-Agent など) を除き、手動で設定できるヘッダは以下に限る:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type
  • Content-Type ヘッダで許可される値は以下に限る:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
注記: これらは Web コンテンツが以前から発行できるクロスサイトリクエストと同種であり、サーバーが適切なヘッダを送信しなければレスポンスデータは送信元へ送られません。従ってクロスサイトリクエストフォージェリを防いでいるサイトは、HTTP アクセス制限について新たに心配することはありません。

例えば、ドメイン http://foo.example にある Web コンテンツがドメイン http://bar.other にあるコンテンツを呼び出したいと仮定します。以下のようなコードを、foo.example に置かれる JavaScript 内で使用するでしょう:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/public-data/';
   
function callOtherDomain() {
  if(invocation) {    
    invocation.open('GET', url, true);
    invocation.onreadystatechange = handler;
    invocation.send(); 
  }
}

この場合ブラウザはサーバーに何を送信し、またサーバーからのレスポンスはどうなるかを見てみましょう:

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61 
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

[XML Data]

1 - 10 行目は、Firefox 3.5 が送信したヘッダです。ここに記載した中で主要な HTTP ヘッダは、10 行目の Origin: ヘッダであることに注意してください。これは、呼び出しがドメイン http://foo.example にあるコンテンツから来たことを表します。

13 - 22 行目はドメイン http://bar.other にあるサーバーからの HTTP レスポンスです。レスポンスでは、サーバーが 16 行目にある Access-Control-Allow-Origin: ヘッダを返します。Origin: ヘッダと Access-Control-Allow-Origin: ヘッダの使用は、もっともシンプルな形のアクセス制御プロトコルを表します。このケースでは、サーバーはリソースがすべてのドメインからクロスサイト方式でアクセスできることを意味する Access-Control-Allow-Origin: * を伴って返答しています。http://bar.other のリソース所有者がリソースへのアクセスを http://foo.example からだけに制限したい場合は、以下のように返答します:

Access-Control-Allow-Origin: http://foo.example

ここで、http://foo.example (前出の 10 行目のようにリクエストの ORIGIN: ヘッダで識別されます) 以外にクロスサイト方式でリソースにアクセスできるドメインはないことに注意してください。Access-Control-Allow-Origin ヘッダには、リクエストの Origin ヘッダで送られた値を含むべきです。

プリフライトリクエスト

シンプルなリクエスト (前述) とは異なり、"プリフライト" リクエストは始めに、実際のリクエストを送信しても安全かを確かめるために他ドメインのリソースへ向けて OPTIONS メソッドを使用して HTTP リクエストを送信します。クロスサイトリクエストはユーザーデータに影響を与える可能性があるため、このようにプリフライトを行います。特に以下のようなリクエストでプリフライトを行います:

  • GETHEADPOST 以外のメソッドを使用した場合。また application/x-www-form-urlencodedmultipart/form-data、または text/plain 以外の Content-Type とともに POST を使用してリクエストを行う場合、例えば application/xml または text/xml を使用して POST で XML のペイロードをサーバーへ送るときは、リクエストでプリフライトを行います
  • カスタムヘッダをリクエストに設定した場合 (例えば、X-PINGOTHER のようなヘッダを用いるリクエスト)。

注記: Gecko 2.0 より、text/plainapplication/x-www-form-urlencoded、および multipart/form-data の各データエンコード形式を、プリフライトなしにクロスサイト送信できます。以前は、text/plain だけがプリフライトなしに送信可能でした。

この種類の呼び出し例は以下のとおりです:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
    
function callOtherDomain(){
  if(invocation)
    {
      invocation.open('POST', url, true);
      invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
      invocation.setRequestHeader('Content-Type', 'application/xml');
      invocation.onreadystatechange = handler;
      invocation.send(body); 
    }
}

......

上記の例の 3 行目では、8 行目の POST で送信する XML の本体を作成しています。また 9 行目では、"カスタム" (非標準) HTTP リクエストヘッダ (X-PINGOTHER: pingpong) を設定しています。このようなヘッダは HTTP/1.1 プロトコルに含まれていませんが、Web アプリケーションで一般的に有用なものです。リクエスト (POST) で application/xml の Content-Type を使用するため、およびカスタムヘッダを設定しているため、このリクエストではプリフライトを行います。

クライアントとサーバーとの間のやりとりの全容を見てみましょう:

OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache

<?xml version="1.0"?><person><name>Arun</name></person>


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some GZIP'd payload]

上記の 1 - 11 行目は OPTIONS メソッドによるプリフライトを表します。Firefox 3.1 は前出の JavaScript コードで使用しているリクエストのパラメータに基づいて、プリフライトの送信が必要であることを判断します。これによりサーバーは実際のリクエストパラメータによって送られるリクエストを受け入れ可能かを応答できます。OPTIONS はサーバーから付加的な情報を得るために用いる HTTP/1.1 のメソッドであり、またべき等、つまりリソースを変更するためには使用できないメソッドです。OPTIONS リクエストと合わせて、他にリクエストヘッダを 2 つ送信していることに注意してください (それぞれ 10 行目と 11 行目です):

Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

Access-Control-Request-Method ヘッダは、実際のリクエストは POST リクエストメソッドで送られることを、プリフライトリクエストの一部でサーバーに通知します。Access-Control-Request-Headers ヘッダは、実際のリクエストに X-PINGOTHER カスタムヘッダおよび Content-Type カスタムヘッダが含まれることをサーバーに通知します。ここでサーバーは、この状況下でリクエストの受け入れを望むかを判断する機会を得ます。

上記の 14 - 26 行目はサーバーが返すレスポンスであり、リクエストメソッド (POST) とリクエストヘッダ (X-PINGOTHER) を受け入れられることを示しています。特に、17 - 20 行目を見てみましょう:

Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400

サーバーは Access-Control-Allow-Methods を返しており、これは当該リソースへの問い合わせで POSTGET、および OPTIONS が実行可能なメソッドであることを伝えます。このヘッダは HTTP/1.1 の Allow: レスポンスヘッダ に似ていますが、こちらはアクセス制御でのみ使用されることに注意してください。

またサーバーは、値が X-PINGOTHER である Access-Control-Allow-Headers を送信しています。これは実際のリクエストで使用できるヘッダであることを認めるものです。Access-Control-Allow-Methods と同様に、Access-Control-Allow-Headers は受け入れ可能なヘッダをコンマ区切りのリストで表します。

最後に Access-Control-Max-Age は、プリフライトリクエストを再び送らなくてもいいように、プリフライトのレスポンスをキャッシュしてよい時間を秒数で与えます。この例では 86400 秒、つまり 24 時間です。ただしこの値には ブラウザごとに設定されている上限値 が適用されます。

クレデンシャルを含むリクエスト

XMLHttpRequest とアクセス制御により公になるもっとも興味深い機能は、HTTP Cookie や HTTP 認証情報を認識する、"クレデンシャルを含む" リクエストを作成できることです。デフォルトではクロスサイト XMLHttpRequest の呼び出し時に、クレデンシャルの送信ができませんXMLHttpRequest オブジェクトを呼び出すときに特定のフラグをセットする必要があります。

以下の例では http://foo.example から読み込まれた元のコンテンツが、http://bar.other にあるリソースに対して Cookie をセットしたシンプルな GET リクエストを行います。foo.example のコンテンツは以下のような JavaScript を含むでしょう:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';
    
function callOtherDomain(){
  if(invocation) {
    invocation.open('GET', url, true);
    invocation.withCredentials = true;
    invocation.onreadystatechange = handler;
    invocation.send(); 
  }
}

7 行目に Cookie を含めて呼び出すためにセットしなければならない XMLHttpRequest のフラグ、すなわち真偽値 withCredentials があります。デフォルトでは、Cookie なしで呼び出しを行います。これはシンプルな GET リクエストであるためプリフライトは行いませんが、ブラウザは Access-Control-Allow-Credentials: true ヘッダを持たないレスポンスを拒否し、呼び出している Web コンテンツが使用できるレスポンスを作成しません

以下はクライアントとサーバーとの間のやりとりの例です:

GET /resources/access-control-with-credentials/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/credential.html
Origin: http://foo.example
Cookie: pageAccess=2


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:34:52 GMT
Server: Apache/2.0.61 (Unix) PHP/4.4.7 mod_ssl/2.0.61 OpenSSL/0.9.7e mod_fastcgi/2.4.2 DAV/2 SVN/1.4.2
X-Powered-By: PHP/5.2.6
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 106
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain


[text/plain payload]

11 行目に http://bar.other 向けの Cookie が含まれていますが、bar.other が Access-Control-Allow-Credentials: true (19 行目) をレスポンスに含めなければ、レスポンスは無視され Web コンテンツで使用できません。重要: クレデンシャルを含むリクエストへ応答するとき、サーバーはドメインを指定しなければならず、ワイルドカードは使用できません。ヘッダで Access-Control-Allow-Origin: * のようにワイルドカードを使用すると、上記の例は失敗します。Access-Control-Allow-Originhttp://foo.example を明示しているため、クレデンシャルを認識したコンテンツが呼び出し元の Web コンテンツ側に返ります。22 行目でさらに Cookie を設定していることに注目してください。

これらすべての例の動作を こちらで確認できます。次章では、実際の HTTP ヘッダについて説明します。

HTTP レスポンスヘッダ

ここでは Cross-Origin Resource Sharing の仕様で定義されている、アクセス制御のためにサーバーが返す HTTP レスポンスヘッダを掲載します。前の章では、これらの実際の動作の概要を説明しました。

Access-Control-Allow-Origin

返されたリソースには、下記構文の Access-Control-Allow-Origin ヘッダがあります。

Access-Control-Allow-Origin: <origin> | *

パラメータ origin は、そのリソースにアクセスしてよい URI を指定します。ブラウザはこれを守らなければなりません。クレデンシャルを含まないリクエストにおいて、サーバーはワイルドカードとして "*" を指定でき、それによりすべてのオリジンにリソースへのアクセスを許可します。

例えば http://mozilla.com にリソースへのアクセスを許可するには、以下のように指定します:

Access-Control-Allow-Origin: http://mozilla.com

サーバーが "*" 以外のオリジンホストを指定した場合は、Origin リクエストヘッダの値に応じてサーバのレスポンスが異なることをクライアントへ示すために、Vary レスポンスヘッダに Origin を含めなければなりません。

Access-Control-Expose-Headers

Gecko 2.0 が必要(Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)

このヘッダで、ブラウザが使用できるヘッダのホワイトリストをサーバーが示すことができます。例えば:

Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header

これは、ブラウザに対して X-My-Custom-Header および X-Another-Custom-Header ヘッダを許可します。

Access-Control-Max-Age

このヘッダはプリフライトの結果をキャッシュしてよい時間を示します。プリフライトリクエストの例は、前出の例をご覧ください。

Access-Control-Max-Age: <delta-seconds>

パラメータ delta-seconds は、結果をキャッシュしてよい時間を秒数で示します。

Access-Control-Allow-Credentials

credentials フラグが真であるときに、リクエストへのレスポンスを開示してよいか否かを示します。プリフライトリクエストのレスポンスの一部として用いたときは、実際のリクエストでクレデンシャルを使用してよいか否かを示します。シンプルな GET リクエストはプリフライトを行いませんので、リソースへのリクエストがクレデンシャル付きで行われた場合にリソースと共にこのヘッダを返さなければ、レスポンスはブラウザによって無視されて Web コンテンツに返らないことに注意してください。

Access-Control-Allow-Credentials: true | false

クレデンシャルを含むリクエスト については前述のとおりです。

Access-Control-Allow-Methods

リソースへのアクセス時に許可するメソッドを指定します。これはプリフライトリクエストのレスポンスで用いられます。リクエストのプリフライトを行う条件については前述のとおりです。

Access-Control-Allow-Methods: <method>[, <method>]*

ブラウザにこのヘッダを送信する例を含む、プリフライトリクエストの例は 前出のとおりです

Access-Control-Allow-Headers

実際のリクエストでどの HTTP ヘッダを使用できるかを示すために、プリフライトリクエスト のレスポンスで使用します。

Access-Control-Allow-Headers: <field-name>[, <field-name>]*

HTTP リクエストヘッダ

ここでは、Cross-Origin Resource Sharing を使用するために HTTP リクエストを発行する際に、クライアントが使用できるヘッダを掲載します。サーバーへ要求を行う際に、これらのヘッダをセットすることに注意してください。クロスサイト XMLHttpRequest 機能を使用する開発者は、Cross-Origin Sharing リクエストヘッダをプログラムでセットする必要がありません。

Origin

クロスサイトのアクセスリクエストやプリフライトリクエストのオリジンを示します。

Origin: <origin>

origin は、リクエストを開始したサーバーを示す URI です。ここにパス情報は含めず、サーバー名だけにします。

注記: origin は空文字列にすることができます。これは、例えばオリジンが data URLである場合などに有用です。

すべてのアクセス制御リクエストにおいて、ORIGIN ヘッダは常に送信されることに注意してください。

Access-Control-Request-Method

実際のリクエストを行う際に使用する HTTP メソッドをサーバーがわかるようにするため、プリフライトリクエストを発信する際に使用します。

Access-Control-Request-Method: <method>

使用例は 前出のとおりです

Access-Control-Request-Headers

実際のリクエストを行う際に使用する HTTP ヘッダをサーバーがわかるようにするため、プリフライトリクエストを発信する際に使用します。

Access-Control-Request-Headers: <field-name>[, <field-name>]*

使用例は 前出のとおりです

仕様

仕様書 策定状況 コメント
Fetch
CORS の定義
現行の標準 CORS 仕様の置き換えを目指して、新たに定義
CORS 勧告 最初期の定義

ブラウザ実装状況

機能 Chrome Firefox (Gecko) Internet Explorer Opera Safari
基本サポート 4 3.5 8 (XDomainRequest を通して)
10
12 4
機能 Android Chrome for Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
基本サポート 2.1 yes yes ? 12 3.2

注記

Internet Explorer 8 および 9 は XDomainRequest オブジェクトで CORS を提供していますが、完全な実装は IE 10 で行っています。Firefox 3.5 よりクロスサイトの XMLHttpRequests と Web フォントのサポートを始めていましたが、ある種のリクエストは後のバージョンまで制限されていました。具体的には Firefox 7 から Web GL テクスチャのクロスサイト HTTP リクエスト、Firefox 9 では drawImage を用いる Canvas への画像描画のサポートを始めました。

関連情報

ドキュメントのタグと貢献者

 このページの貢献者: hamasaki, unarist, yyss, RobinEgg
 最終更新者: hamasaki,