導入:「CORSエラー」との不毛な戦い
フロントエンド(React)からバックエンドAPIを叩いた瞬間、真っ赤なエラーがコンソールに殴り込んできます。
Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header...
「CORS…お前またか…」
開発中にこのエラーに遭遇した時、私たちがやりがちなこと。それは、バックエンドの設定ファイルを開いて、こう書くことです。
# Django
CORS_ALLOW_ALL_ORIGINS = True
# Express
app.use(cors()); // デフォルトで全オリジン許可
「よし!エラー消えた!動いた!」
この「とりあえず全許可」が、本番環境にそのまま残った時、あなたのサービスに何が起きるか。今日はその恐怖を解説します。
技術解説:CORSは「ブラウザが張っている結界」である
そもそもCORSとは何か
ブラウザには**「同一オリジンポリシー(Same-Origin Policy)」**という、Webセキュリティの大前提となるルールが組み込まれています。
「あるオリジン(ドメイン)のWebページから、別のオリジンのリソースへのアクセスをデフォルトでブロックする」
例えば、https://evil.com に埋め込まれたJavaScriptが、ユーザーのログイン済みCookieを使って https://api.your-service.com/users/me のデータを fetch しようとしても、ブラウザが「オリジンが違うからダメ!」と弾いてくれます。これが「結界」です。
CORS(Cross-Origin Resource Sharing)は、この結界に「穴」を開ける仕組みです。
サーバーが Access-Control-Allow-Origin: https://your-frontend.com というヘッダーを返すことで、「このオリジンからのアクセスだけは許可するよ」とブラウザに伝えます。
*(ワイルドカード)の恐怖
Access-Control-Allow-Origin: * は、「この世の全てのWebサイトからのアクセスを許可する」という意味です。
これを認証付きのAPI(withCredentials: true / credentials: 'include')で使うとどうなるか。
- ユーザーが
your-service.comにログインしている(Cookieを持っている) - ユーザーが
evil.comを踏む -
evil.comのJavaScriptがfetch('https://api.your-service.com/users/me', {credentials: 'include'})を実行 - ブラウザは「
*で全許可だし、Cookie付きリクエストを送ってOK」と判断 -
ユーザーの個人情報がレスポンスとして
evil.comのJavaScriptに渡される
(※実際にはブラウザの仕様でcredentials: includeと*の組み合わせはブロックされますが、一部のバックエンドがOriginヘッダーをそのままエコーする実装をしている場合、この防御は突破されます)
「Originエコー」という最悪のパターン
# 最悪のアンチパターン:リクエストのOriginをそのまま返す
response['Access-Control-Allow-Origin'] = request.headers['Origin']
response['Access-Control-Allow-Credentials'] = 'true'
これは「全てのオリジンを信頼する」のと同義であり、* よりも遥かに危険です。攻撃者のオリジンがそのまま許可オリジンとして返されるため、Cookie付きリクエストの制限も回避されてしまいます。
CORSを正しく設定するための鉄則
1. 許可オリジンをホワイトリストで管理する
ALLOWED_ORIGINS = ['https://your-frontend.com', 'https://staging.your-frontend.com']
if request.headers['Origin'] in ALLOWED_ORIGINS:
response['Access-Control-Allow-Origin'] = request.headers['Origin']
2. 開発環境と本番環境の設定を分離する
CORS_ALLOW_ALL_ORIGINS = True は開発環境の .env だけに留め、本番の設定ファイルには絶対に含めない仕組み(環境変数での切り替え)を作りましょう。
3. null オリジンを許可しない
Access-Control-Allow-Origin: null も危険です。ローカルHTMLファイルや data: URI、サンドボックスiframeからのリクエストは Origin: null を送信するため、攻撃に悪用される可能性があります。
CORSエラーは「邪魔な門番」ではありません。それはあなたのユーザーを守る「最後の砦」です。開発中のストレスに負けて砦を取り壊すのではなく、正しい通行証(ホワイトリスト)を発行してあげてください。