この記事は 脆弱エンジニアの Advent Calendar 2025 4日目の記事です!
はじめに
Webフロントエンドを触っていて、いざAPIにアクセスするぞと思ったとき、こういうエラーが出たことって、ありますよね。
Access to fetch at ****** from origin ****** has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
なんか調べるとサーバー (API) 側でこういうヘッダを設定してあげると直るらしいですね。
Access-Control-Allow-Origin: *
これ自体は間違いではないのですが、これだと CORS が邪魔者みたいですよね。でも実は CORS は Web の世界においてセキュリティを維持する重要な役割を担っているのです。
この記事では
- 誰が
- なぜ
- どのように
CORS の制限を課しているのかについてより詳しく掘り下げていこうと思います。
この記事では気持ちの理解に重点を置いて説明しているため、各用語の説明や細かい挙動の説明などは意図的に省略しています。詳細についてはMDNなどのドキュメントを用いて補完してください。
CORS #とは
CORS とはブラウザに実装されたセキュリティ機構です。JavaScriptから送られたAPIリクエストをブラウザがブロックしてエラーにしています。
逆に言えばサーバーサイドからリクエストを送ったり、curl やら Postman やらを使ってリクエストを送るときには CORS は関係ありません。
そして CORS の役割は 「異なるオリジン間でのリソースの共有を適切に行えるようにすること」 です。これだけだと分からないと思うので、もう少し掘り下げていきましょう。
この説明は CORS と SOP をやや混同していますが、次の章以降で詳しく説明していきます。
CORS は何をするのか
CORS の役割はリソースの共有を適切に行えるようにすることでした。逆に不適切なリソースの共有というのはどういう状況を指すのでしょうか?具体例を見ていきましょう。
https://bank.example で銀行アプリが動いていて、https://bank.example/balance をAPIで叩くと残高が取得できるとします。攻撃者が残高APIにアクセスする https://attacker.example というサイトを用意して、銀行アプリの利用者がそのサイトにアクセスしたらどうなるでしょうか?
勝手に残高を盗み見られるのは困るので、これは不適切なリソースの共有と言えます。ここでのリソースは https://bank.example/balance にアクセスして得られるレスポンス、すなわち残高情報のことを指します。
幸運なことに、現代のWebブラウザであればこのような不適切なアクセスは通常ブロックされます。このアクセスをブロックする仕組みを SOP (Same-Origin Policy) と呼びます。ユーザーがアクセスしたサイト (https://attacker.example) とJavaScriptによってアクセスしようとしているサイト (https://bank.example/balance) のオリジンが異なるため、SOP に従ってリクエストをブロックします。
ここで https://myapp.example にアプリのフロントエンドを立てて、そこから https://api.myapp.example のAPIサーバーにアクセスするようなケースを考えてみます。アプリのプロフィールページなどに表示するために https://api.myapp.example/me からログインしているユーザーの情報を取得しようとします。
これは適切なリソースの共有に見えます。なのでリクエストは通って欲しいのですが、先と同じ理屈で SOP によって弾かれてしまいます。
これは困りました。これを上手く回避する方法が CORS です。1
CORS ではリソースを持っている/返す側2がどのオリジンからアクセスされて良いかを制御します。この制御には CORS ヘッダと呼ばれるヘッダを用います。例えば冒頭で出てきた Access-Control-Allow-Origin: * というのは全てのオリジンからのアクセスを許可するという意味です。
myapp の例で言えば、https://api.myapp.example からのレスポンスに Access-Control-Allow-Origin: https://myapp.example というヘッダを付ければ良いことが分かります。
なぜ SOP / CORS が必要なのか
ブラウザは変なWebサイトを開いても「安全」である必要性があります。この「安全」というのは様々な側面があるのですが、その1つとしてサイト間での干渉を防ぐというものがあります。
現代は様々なことがWeb上で行われるようになりましたが、これはサイト間で適切にリソース3が分離されていることによって安全性が担保されています。逆にサイト間で干渉が起きてしまうと、例えば攻撃者のサイトにアクセスすると、そこから銀行アプリにアクセスされて残高を盗み見られてしまう、といった攻撃が成立してしまいます。これを防ぐために SOP が存在しています。
SOP はブラウザのデフォルトの挙動です。ブラウザは安全を保つためにデフォルトではリクエストを厳しく弾きます。しかし myapp / api.myapp の例のように、開発者の意図したアクセスに限り許可するために CORS を使います。
このように SOP / CORS はブラウザの非常に基本的な安全性を担保するために欠かせない機能なのです。
しかしこれは信頼できないJavaScriptを実行するというブラウザの都合でしかないので、ブラウザ以外の環境 (curlなど) で開発者の意図したリクエストしか送らない場合には全く必要ありません。4
まとめ
開発をしていて CORS に阻まれてしまうと「ムッ」となるかもしれませんが、ブラウザの安全性を保つための重要な仕組みによって仕方なくこうなっているのです。今度から CORS エラーを見かけたら「今日もWebを安全にしていて偉いね ヨシヨシヾ(・ω・`)」という気持ちで見てあげてください。
この記事では意図的に CORS の詳しい設定の仕方などについては触れていませんでしたが、その辺りについては MDN や他のブログなどを参考にしていただけると幸いです。



