CORSについて改めてまとめてみる
初見で遭遇した場合、頭を抱えてしまうCORSエラー
WebアプリケーションからAPIを叩くとき、APIがCORSというエラーを吐くことがある。
CORSについて理解していない場合、なんのこっちゃかわからないため解決策が全くもってわからない。
筆者は新卒で入社した会社で当初フロントエンドの業務を行っており、サーバーサイド側の知見がほぼなく、ネットで解決策を漁ることとなった。
(自分がフロントエンド業務だからと言って、サーバーサイド側の勉強を放置しないようにする。自分と同じ痛い目に遭います。)
CORSエラーをわかりやすく書くと、「XHTTPRequestで自身のドメインと違うドメインに対してアクセスするな
、同じ生成元のドメインに対してAPIを叩いてくれないと、レスポンスデータなんて読み込めない。」って感じだろうか。
同一生成元ポリシー
ではなぜ同一生成元のドメインでないと、APIを叩けないのか。
→ これは同一生成元ポリシー(Same Origin Policy) というセキュリティポリシーによるもの。
この同一生成元ポリシーは、
同じ生成元(オリジン)からのアクセスのみ許可する
というルールで、CORSエラーはこれに引っかかって吐かれるエラーである。
この生成元とは何かというと、いわゆるURLである。
詳しくはURL中のスキーム(http, https)、ホスト(api.example.com)、ポート番号の組み合わせで判断されるものである。
従って、例えば http://api.example.com / http://www.example.com の組み合わせや、
http://api.example.com / https://api.example.com の組み合わせはもちろん異なる生成元であり、同一生成元ポリシー下ではCORSエラーが吐かれる対象である。
ブラウザからコールがかかるAPIをhttps://api.example.com のようにAPI単体のドメインとして構築したとて、XHTTPRequestを行うことはもちろんできない。
(そこで同一生成元ポリシーの回避策としてJSONPなどの手法が開発されたが、JSONPとかはセキュリティ上の問題が多く、少しナンセンス。)
クロスオリジンリソース共有
そこで異なる生成元にアクセスするための手法として策定されたのが、
クロスオリジンリソース共有(CORS: Cross-Origin Resource Sharing) である。
このCORSを利用することにより、異なる生成元からのアクセスに対して、特定の生成元からのアクセスのみに対し、アクセスを許可することが可能となった。
(JSONPよりもセキュリティの観点で安全)
CORSの基本的なムーブ
まずCORS対応を行うにあたって、クライアントからOriginというリクエストヘッダを送信する必要がある。このヘッダにはアクセス元(APIを叩くドメイン)となる生成元を指定する。
例えば、http://www.example.com から http://api.example.com というAPIを叩く場合、リクエストヘッダのOrginには http://www.example.com が指定される。
Origin: http://www.example.com
サーバー側(APIを実装する側)はあらかじめアクセスを許可する生成元のリストを持っており、アクセスしてきたリクエストヘッダのOriginを確認し、それが生成元のリストに入っているかを確認する。
もし生成元のリストに含まれていた場合、Access-Control-Allow-Orogin というレスポンスヘッダに、Originリクエストヘッダと同じ生成元を格納して返却することで、アクセスが許可されたことを示す。
Access-Control-Allow-Origin: http://www.example.com
もし生成元のリストに含まれていなければ、ステータスコード403 でエラーを返す。
ちなみにAccess-Control-Allow-Originヘッダに * を指定することで、いかなる生成元ドメインからのアクセスが許可される。
PJの新規要件で新しいAPIが開発される際などでは、Stub-API(本番環境用ではないデモ版のAPI)がよくこのCORS設定になっている気がする。
GitHubの一般的なAPIとかは、CORS対応で * が指定されている。
Access-Control-Allow-Origin: *
CORSとユーザー認証
CORS対応でユーザー認証を送信する場合、追加のHTTPレスポンスヘッダを発行する必要がある。
例えば、クライアント側(ユーザー)がCookieヘッダやAuthenticationヘッダを使って認証情報を送信してきた場合、サーバー側は、Access-Control-Allow-Credentialsヘッダにtrueをセットし、「認証情報確認しましたよ。」という旨をクライアント側に送っている。
Access-Control-Allow-Credentials: true
おまけ フロントエンド業務でCORSエラーが発生したら
もしフロントエンド業務で他チーム(バックエンドチーム? サーバーサイドチーム? PFチーム?)
から納品されたAPIを正しいリクエスト方法で叩いているのにも関わらずCORSエラーが吐かれる場合、クライアント側での実装では基本的にどうすることもできないので、APIの実装担当者に問い合わせ案件である。
Discussion