9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CORSの誤解を解く:読み取り防御としての本質と限界を整理してみた

Last updated at Posted at 2025-06-22

CORSとは何か? 仕組みと対策をざっくり整理してみた

CORSって単語はよく聞くけど、調べるたびにモヤモヤして、根っこから理解できていない感じがずっとありました。
今回改めて整理してみたらスッと腹落ちしたので、自分なりにまとめておきます。同じように「名前は知ってるけど納得できてない」方の助けになれば嬉しいです。

✅ 対象読者とこの記事のスタンス

この投稿は、以下のような方を対象にしています:

  • フロントエンド or フルスタック初学者で「CORSってよく聞くけど、結局なんなの?」と思っている方
  • 開発中に「CORSエラー出た!」と焦った経験があり、背景から理解したい方
  • APIを設計・実装していて、CORS対応をざっくり整理しておきたい方

この記事で紹介すること:

  • CORSの基本的な仕組みと目的
  • エラーの発生理由とその回避方法
  • プリフライトリクエストの意味と発生条件
  • 「CORSが防げること・防げないこと」の切り分け

この記事では扱わないこと:

  • 各フレームワーク(Express, Railsなど)での実装方法詳細
  • CDNやAPI GatewayによるCORS設定の実践例
  • セキュリティの全体像(CSRFや認証認可全体)は必要に応じて触れる程度

🔰 CORSとは?

CORS(Cross-Origin Resource Sharing)とは、リクエスト送信元のオリジン(たとえば window.location.origin で取得できるもの)と、リクエスト送信先(例:fetch("https://example.com/api/items"))のオリジンが異なる場合に、ブラウザが制限をかける仕組みです。

ポイントは次の通り:

  • レスポンスを JavaScript から読めるか を制御するものであり、リクエスト自体を止めるものではない
  • あくまでブラウザの機能なので、curl などのCLIツールには関係ない
  • 自作ブラウザなどでCORSの仕組みを無視すれば、そもそもこの制限も回避できてしまう(だからこそ「ブラウザレベルの制約」)

🔒 誰から何を守っているの?

CORSは、主に「ユーザーの認証情報を使ったクロスオリジンの読み取り系攻撃」を防ぐためのものです。

たとえば以下のような攻撃が想定されます:

fetch("https://example.com/api/getBankAccountInfo", {
  credentials: "include"
})
  .then(res => res.json())
  .then(data => steal(data)); // 攻撃者のサーバーへ送る

攻撃者が自分のサイトにアクセスさせ、ユーザーのクッキーを使って機密情報を読み取り、自分のサーバーに送信する――
これを防ぐのがCORSの役割です。

🧱 CORSエラーの仕組みと回避方法

ブラウザはレスポンスヘッダに Access-Control-Allow-Origin が含まれているかをチェックし、許可されたオリジンではない場合、JavaScript からのアクセスを遮断します。

例:

Access-Control-Allow-Origin: https://example.com

⚠️ 注意点:

  • Access-Control-Allow-Origin: * は「どんなオリジンでも通します」という意味になるため、本番環境ではNG(特にクッキーなどの認証情報を扱う場合は非常に危険)。
    一方で、開発中にローカルから確認するようなケースでは、手軽さを優先してOK。
  • APIは、リクエスト自体を受け取ってレスポンスも返しているが、JS側では「アクセス不可」となる
  • サーバー同士の通信にはCORS制約は関係ない(=fetchなどJS経由の話)。

つまり、APIを「ブラウザから呼び出す」なら、必ずCORS対応が必要になります。

補足:Access-Control-Allow-Origin の値はどう決まる?
一部の実装では、リクエストヘッダの Origin を見て「この値なら返す、そうでなければ返さない」と動的に判断する仕組みを取っていることもあります。
最近のSaaS系サービスでは、CORSの制御はインフラや設定画面で完結することも多く、アプリ側でCORSヘッダを動的に組み立てるケースは減ってきています

✈️ プリフライトリクエストとは?

一部のCORSリクエストでは、ブラウザが「このリクエスト送ってもいい?」と事前確認を行います。これをプリフライト(preflight)リクエストと呼びます。

プリフライトが発生する例:

  • Content-Type: application/json を指定
  • カスタムヘッダ(例:X-Requested-Withなど)を使用
  • PUTDELETE などの一部HTTPメソッドを使用

プリフライトリクエストに対して 200 OK など 成功ステータスが返ってこないと、本リクエストも送信されません。

❗️ CORSが守って「いない」もの

重要なのは、CORS は API 自体を守っているわけではないという点です。

以下のようなリクエストは、CORS があっても実際に API に到達してしまいます

fetch("https://example.com/api/deleteBankAccount", {
  method: "POST",
  credentials: "include"
});

これは APIサーバー側のロジックで制限しないと実行され得る ため、CORSとは別に適切な認証・認可を設計する必要があります。
たとえば、以下のような対策があります:

  • CSRFトークン
  • Refererチェック
  • SameSite Cookie設定
  • アクセス制御ロジック(IP制限など)

補足:非GETリクエストも、CORSによって結果的に守られている場合がある
たとえば POSTDELETE のような非GET系のリクエストは、多くの場合プリフライト(事前確認)が発生し、
そのプリフライトに失敗することで本リクエスト自体が行われないというケースもあります。
ただし、それでもCORSは完全な防御ではないため、CSRFトークンなど多重防御は重要です。

✅ まとめ

  • CORSはあくまで「読み取りを制限するブラウザ機能」である
  • 認証情報を悪用される読み取り型の攻撃を防ぐのが目的
  • 本質的なAPI防御には別途サーバー側の防御ロジックが必要
  • 「なんかCORSエラー出た」というときに、仕組みと目的がわかっていると対処がスムーズ!

「"CORS=邪魔なエラー" と感じがちだけど、実はユーザーを守るための仕組み」という視点を持つと、CORSとの付き合い方が少し優しくなるかもしれません。

以上です!


🔐 他のセキュリティ系記事もよかったらどうぞ:

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?