Help us understand the problem. What is going on with this article?

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

参考サイト

umechiki
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away