Edited at

CORS 対応時の注意点などメモ

More than 1 year has passed since last update.

同じオリジンのAPIと非同期で通信、という場合は xhr で普通にこなしてますが、別ドメインのAPIと非同期で、となると割とつまづくポイントがあったのでメモ。

こちらのサイトが大変分りやすかったです。


xhr2 を使う

特に何かしなくても、ブラウザがxhr2対応しているものであればxhr2を使うことになります。

逆にいうと、xhr2 対応していないブラウザだとダメ。


とりあえず普通に実装する

特にクロスドメインであることを気にかけず、クライアントサイド、サーバサイドそれぞれをまずは実装します。


Origin 許可する

サーバサイドでアクセス元となるOriginを意識して設定する必要があります。


  • サーバサイドのレスポンスヘッダの追加

  • クライアントサイドはモダンブラウザなら特別な対応は不要


サーバサイドの対応

Access-Control-Allow-Origin レスポンスヘッダを追加します。

セットする値は許可するアクセス元Originになります。(プロトコル + サブドメイン + ドメイン)

どこからアクセスされてもOKな場合は "*" になります。

クライアント側の環境によってはうまく処理されないケースがあるので、Access-Control-Allow-Headers レポンスヘッダも一緒に追加します。

セットする値はクライアントサイドの実装環境によって、Content-Type の外にも X-Requested-With や Accept や Origin などをセットしたほうがいい時があるみたいです。

以下はHTTP/SSL での foo.unknown.jp と bar.unknown.com という Origin からのアクセスだけを許可する場合の apache 側の設定例です。

SetEnvIf Origin "^https?://(foo¥.unknown¥.jp|bar¥.unknown¥.com)$" ORIGIN=$0

Header append Access-Control-Allow-Origin %{ORIGIN}e env=ORIGIN
Header append Access-Control-Allow-Headers "Content-Type"

Access-Control-Allow-Origin のホワイトリスト対応の件

上の例のような、特定複数のOriginだけ許可したい、という場合に例えばサーバ側から

# 注意: これはうまくいかない

Access-Control-Allow-Origin: "http://foo.unknown.jp http://bar.unknown.com"

となるように返しても、 Access-Control-Allow-Origin ヘッダは1つしか値を受け付けないという内容のエラーメッセージが表示 されます。

複数許可したい、でも "*" にするわけにはいかないという場合、サーバ側でオンデマンドに許可するOriginを変更することで回避できます。


クライアントサイド

とくに意識せずxhrリクエストを投げます。以下は jQuery での例。


jQuery

$.ajax({

url: 'https://foo.unknown.jp/api/test.json',
type: 'get',
dataType: 'json',

success: function(result, textStatus, xhr) {
call_back_function.call(self, result, textStatus, xhr);
},
error: function(xhr, textStatus, error) {
call_back_error_function.call(self, xhr, textStatus, error);
}
});



Cookie 使う場合は更に Credentials を有効にする

Cookie をやり取りしたい場合は、Credentials を有効にする必要があります。


  • サーバサイドのレスポンスヘッダの追加

  • クライアントサイドからのリクエストで credential を有効にする


サーバサイドの対応

Access-Control-Allow-Credentials レスポンスヘッダを追加します。

値は true にします。

Header append Access-Control-Allow-Credentials true

※ Access-Control-Allow-Origin が "*" になっていると使えないようです。


クライアントサイドの対応

リクエスト時にCredentialを付けて送る必要があります。

以下はjQueryでの設定例で、xhrFields プロパティを設定しています。


jQuery

$.ajax({

url: 'https://foo.unknown.jp/api/test.json',
type: 'get',
dataType: 'json',
xhrFields: {
withCredentials: true
},

success: function(result, textStatus, xhr) {
call_back_function.call(self, result, textStatus, xhr);
},
error: function(xhr, textStatus, error) {
call_back_error_function.call(self, xhr, textStatus, error);
}
});


Chrome とFirefoxはここまでの設定で大体動くようになりました。

IEはまだもう少し対応が必要です。


IE 対応するなら利用ライブラリのバージョンを確認

IE9以前はXMLHttpRequest対応していない独自オブジェクトXDomainRequestを使うため、jQueryなどライブラリを使用している場合は使用バージョンでどう対応しているのか確認します。

ここがサポートブラウザに影響してくるかもしれません。


IE でCookie使うなら P3P コンパクトポリシー

IEのセキュリティレベルの設定で、「すべてのCookieを受け付ける」以外にしている場合は、

P3Pコンパクトポリシーの付いているサイトでないとCookieを送信しないようです。

Cookieを発行しているリソースプロバイダが Set-Cookie レスポンスヘッダを返す時に、一緒に P3P ヘッダへコンパクトポリシーをセットして返すようにすれば、セキュリティを最低レベルにする必要がなくなります。

# Cookieの発行サーバで以下をレスポンスヘッダにつける

P3P: CP="NON CUR OUR NOR ONL UNI"

P3Pコンパクトポリシーの設定値

P3Pコンパクトポリシーが設定してあれば、設定値は何でも動いてしまうようです。

とはいえP3Pの仕様を参考にCookie発行サイトがどのようなポリシーなのか、3文字で表すトークンを組み合わせて作ってみると良いと思います。

そんなに難しいことはないですし、間違えてしまっても動きますし。


IE のセキュリティ設定

P3Pコンパクトポリシーに対応している外部サイトの場合には、ユーザがブラウザの設定を変更することなく CORS が使えました。

P3Pコンパクトポリシーが付いていない外部サイトにCORSする場合は、以下のセキュリティ設定の変更をすれば使えるようになりました。実用することはほぼないと思いますが何かの時の参考までに。


  • インターネットオプション -- セキュリティ設定 -- インターネットゾーンのセキュリティレベルの[レベルのカスタマイズ] -- その他 -- ドメイン間でのデータソースのアクセス を有効にする

  • インターネットオプション -- プライバシー設定 -- レベルを一番低い「すべてのCookieを受け入れる」にする


このメモを書いたときの手元の環境

参考までに。

サーバサイド


  • apache 2.2.27 / 2.4.12

クライアントサイド


  • jQuery 2.1.3

  • Chrome 41.0.2272.76

  • Firefox 36.0.1

  • IE 11.0.9600


参考サイト