HTTP access control (CORS)

クロスサイト HTTP リクエスト とは、要求を出すリソースが存在するドメインとは異なるドメインのリソースに対して HTTP リクエストを行うことを指します。例えば、ドメイン A (http://domaina.example) にて読み込まれた HTML ウェブページなどから、img 要素 (http://domainb.foo/image.jpg) を用いてドメイン B (http://domainb.foo) 上の画像に対する HTTP リクエストが発行される、という状況を指します。このような状況は今日の web 上では非常にありふれたことであり、数多くの CSS スタイルシートや画像、スクリプトなどの外部リソースがこのようなクロスサイト HTTP リクエストを用いて読み込まれています。

通常、スクリプトによって開始されるクロスサイト HTTP リクエストについては、セキュリティ上の理由から、よく知られた制限が加えられます。例えば XMLHttpRequest オブジェクトにより発行された HTTP リクエストは 同一生成元ポリシー (same-origin policy) の制約を受けます。特に、XMLHttpRequest を使用した web アプリケーションは自身が読み込まれたドメインにのみ HTTP リクエストを発行でき、他のドメインには発行できない、ということです。よりよく、より安全な web アプリケーションのマッシュアップを作成するべく、開発者たちは XMLHttpRequest などのクロスサイト・リクエスト機能を安全に進化させることを望んできました。

W3CWeb Applications Working Group が、新たな Cross-Origin Resource Sharing (CORS) 勧告を提案しました。これは、web サーバーがサイトを跨ぐアクセスを制御する方法を規定することで、サイト間での安全な通信を保証するというものです。特筆すべき点として、この仕様は XMLHttpRequest のような API コンテナ内で緩和策として使用されることにより、現行のブラウザにおいて同一ドメイン制限を超えることを可能にするということです。この文書において書かれている情報は、web 管理者、サーバー管理者、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 または POST のみ用います。POST をサーバーへのデータ送信に用いる場合、HTTP POST でサーバーに送られるデータの Content-Type は application/x-www-form-urlencodedmultipart/form-data、または text/plain のいずれかです。
  • HTTP リクエストにカスタムヘッダ (X-Modified など) を設定しません。
注記: これらは 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 ヘッダで送られた値を含むべきです。

プリフライトリクエスト

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

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

Gecko 2.0 note
(Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)

Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1) から 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
Access-Control-Max-Age: 1728000
Vary: Accept-Encoding
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
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

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

上記の 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
Access-Control-Max-Age: 1728000

サーバーは 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 は、プリフライトリクエストを再び送らなくてもいいように、プリフライトのレスポンスをキャッシュしてよい時間を秒数で与えます。この例では 1728000 秒、つまり 20 日間です。

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

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
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

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

Access-Control-Expose-Headers

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

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>]*

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

ブラウザ実装状況

機能 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 への画像描画のサポートを始めました。

関連情報

Document Tags and Contributors

Contributors to this page: RobinEgg, yyss
最終更新者: yyss,