日本で今も根強く使われているRuby on Railsには、CORSを扱う標準機能がなく、rack-corsgemを追加してconfig/initializers/cors.rbで明示的に設定する必要がある。よくある落とし穴は、開発中にorigins '*'としたまま本番に出してしまうことだ。これは単純なGETリクエストの間は問題なく動くが、credentials: trueを有効にしてセッションCookieをAPIサブドメインに送ろうとした瞬間、ブラウザがレスポンスを拒否し始める。
同一オリジンポリシー
デフォルトでは、ブラウザは同一オリジンポリシーを適用する:あるオリジン(ドメイン+プロトコル+ポート)のページのJavaScriptコードは、別のオリジンへのリクエストのレスポンスを読み取ることができない。これは、あるサイトの悪意あるコードが、ユーザーがログインしている別のサイトからデータを盗むことを防ぐための基本的なセキュリティ機構だ。
CORSがこのポリシーを緩和する仕組み
CORS(Cross-Origin Resource Sharing)は、サーバーが特定の(またはすべての)外部オリジンに対して自身のレスポンスの読み取りを明示的に許可するためのHTTPヘッダーの集合だ。主要なヘッダーはAccess-Control-Allow-Originで、許可されたドメインを指定する。
ワイルドカードとcredentialsの組み合わせという罠
別のサブドメインのAPIにセッションCookieを送るために必要なcredentials: 'include'付きのリクエストを送ると、ブラウザはレスポンスのAccess-Control-Allow-Origin: *をきっぱり拒否する。仕様はワイルドカードオリジンとcredentialsの組み合わせを明示的に禁じており、サーバーはリクエスト元の正確なオリジンを返さなければならない。
なぜ必要なのか
- CORSエラーを診断し、サーバー側で正確にどのヘッダーが不足しているかを把握する。
- Railsの
rack-cors設定に残った開発用ワイルドカードを本番投入前に見つける。 - 「シンプル」なリクエストとpreflight(OPTIONS)リクエストの違いを理解する。
CORSはサーバーを攻撃から守らない
CORSがサーバー側のセキュリティ対策だと考えるのはよくある誤解だ — 実際にはブラウザ内のJavaScriptが読み取れる内容を制限しているに過ぎない。curlやPostman、あるいはバックエンドスクリプトを直接使う攻撃者は、これらのツールが同一オリジンポリシーを適用しないため、CORSを完全に迂回できる。CORSは決してサーバー側の本物の認証・認可の代わりにはならない。