CORS(Cross-Origin Resource Sharing)

目次

CORSとは

クロスサイトリクエストフォージェリ(CSRF)などのセキュリティ攻撃を防止するために、ブラウザは「同一生成元ポリシー(Same-Origin Policy)」という仕組みを実装し、異なるオリジンのリソースへのアクセスに制約をかけています。CORS (Cross-Origin Resource Sharing)は、この制約を一部解除し、異なるオリジン間でリソースを共有するための仕組みです。

CORS の仕組み

例えば、Site-A(site-a.example.com) から他オリジンの Site-B(site-b.example.com) のリソースやAPIを参照したい場合、Site-A を閲覧中のブラウザは Site-B へのリクエストヘッダにアクセス元のオリジン情報を付加します。XMLHttpRequest によるアクセスや、crossorigin="anonymous" を指定した img, script, audio, video, link アクセスの場合などに、Origin: ヘッダが付与されます。

ブラウザ(Site-A) → Site-B
Origin: http://site-a.example.com

これに対し、site-b 側で、site-a へのアクセスを許可する場合、下記の様なレスポンスヘッダを返します。

Site-B → ブラウザ(Site-A)
Access-Control-Allow-Origin: http://site-a.example.com

すべてのオリジンに対してアクセスを許可する場合、ワイルドカード(*) を使用することができます。

Site-B → ブラウザ(Site-A)
Access-Control-Allow-Origin: *

単純リクエストとプリフライトリクエスト

上記のやりとりは「単純リクエスト」と呼ばれるもので、下記の条件が満たされる場合に利用可能なシーケンスです。

上記の条件を満たせない場合は、「プリフライトリクエスト」と呼ばれるもう少し複雑なシーケンスとなります。下記は、条件に含まれてないヘッダ (X-Custom-Header) を送信する例です。まず、Site-A を閲覧中のブラウザ は Site-B に対して、リクエストしたい情報を OPTIONS で事前確認します。

ブラウザ(Site-A) → Site-B
OPTIONS /some-url HTTP/1.1
Origin: http://site-a.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-Custom-Header, Content-Type

これに対し、Site-B は、アクセスを許可する・しないの情報を返します。

Site-B → ブラウザ(Site-A)
Access-Control-Allow-Origin: http://site-a.example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-Custom-Header, Content-Type
Access-Control-Max-Age: 86400

アクセスの許可が得られたのちに、ブラウザは実際のリクエストを送信します。

ブラウザ(Site-A) → Site-B
POST /some-url HTTP/1.1
Content-Type: text/json
X-Custom-Header: custom-data

Site-B がこれに応答します。

Site-B → ブラウザ(Site-A)
HTTP/1.1 200 OK

Access-Control-Max-Age で指定した時間が過ぎるまでは、OPTIONS による事前確認なしに POST することができます。

資格情報を含むリクエスト

Site-A から Site-B に Site-A の Cookie や HTTP認証による認証情報を渡す場合は、Site-A のスクリプト側で withCredentials フラグを true にし、Site-B からの OPTIONS 応答で Access-Control-Allow-Credentials: true が返却される必要があります。

Site-A
<script>
window.addEventListener('load', function() {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            console.log("status = " + xhr.status);
            console.log(xhr.responseText);
        }
    }
    xhr.open("POST", "http://site-b.example.com/");
    xhr.withCredentials = true;
    xhr.setRequestHeader("Content-Type" , "text/json");
    xhr.send(JSON.stringify({"sid": getCookie("sid")}));
});
</script>

Site-A を閲覧中のブラウザは通常通りの事前確認用の OPTIONS を送信します。

ブラウザ(Site-A) → Site-B
OPTIONS /test HTTP/1.1
Origin: http://site-a.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-Custom-Header, Content-Type

Site-B は、Access-Control-Allow-Credentials: true を返却します。

Site-B → ブラウザ(Site-A)
Access-Control-Allow-Origin: http://site-a.example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-Custom-Header, Content-Type
Access-Control-Max-Age: 86400
Access-Control-Allow-Credentials: true

これにより、Site-A の JavaScript は、Cookie などの認証情報を Site-B に POST することが許可されます。