【実務・中級編】Cross-Origin Resource Sharing (CORS) の安全なポリシー設計 – アプリケーションセキュリティ & 安全な開発防御ガイド

CORSは「ブラウザの善意」に頼るな。防御の最前線で語る実務的なポリシー設計

現場でインシデント対応をしていると、必ずと言っていいほど遭遇するのが「CORS(Cross-Origin Resource Sharing)の設定不備」だ。

「とりあえず動けばいい」と`Access-Control-Allow-Origin: `を設定し、Credential(認証情報)を通すために慌てて`Access-Control-Allow-Credentials: true`を付け足す。これ、攻撃者からすれば「どうぞ、私のデータをご自由に盗んでください」という招待状を配っているようなものだ。

今日は、教科書的な説明は抜きにして、なぜCORSの設定ミスが致命的なのか、そして現場でどう実装すべきか、プロの視点で深掘りする。

1. なぜ「ワイルドカード」と「Credential」の組み合わせが地獄を見るのか

CORSはブラウザが提供するセキュリティ機能であり、サーバー側で「どのオリジンからのリクエストを許可するか」を決めるものだ。

ここで最大のリスクは、`Access-Control-Allow-Origin: ` と `Access-Control-Allow-Credentials: true` は決して共存させてはならないというブラウザの仕様を無視することにある。

攻撃シナリオ:CSRFを超える「データ窃取」

攻撃者は、被害者がログイン中のWebアプリに対し、不正なスクリプトを埋め込んだページを閲覧させる。
1. 被害者のブラウザから、ターゲットのAPIへCookie付きのリクエストが飛ぶ。
2. もしAPIが不用意に `Origin` ヘッダーをそのまま反射(Reflect)させていたり、ワイルドカードで許可していれば、ブラウザは「許可されている」と判断してレスポンスをJavaScriptに渡してしまう。
3. 結果: APIが返すユーザーの個人情報や機密データが、攻撃者のサーバーへそのまま送信される。

これを防ぐための鉄則はただ一つ。「必要なオリジンをホワイトリスト形式で厳密に管理する」ことだ。

2. 実装:Nginxとバックエンドの「正しい」制御

CORS設定は、アプリケーションコードだけで解決しようとせず、可能な限りゲートウェイ(Nginx)側で制御するのが運用上のベストプラクティスだ。これにより、アプリケーションの脆弱性による設定漏れを防げる。

Nginxでのセキュアな設定例

特定のドメインのみを許可する堅牢な実装だ。

許可するオリジンのリスト(正規表現で定義)
map $http_origin $allow_origin {
default “”;
“~^https://(www\.)?your-trusted-app\.com$” “$http_origin”;
“~^https://api\.your-trusted-app\.com$” “$http_origin”;
}

server {
location /api/ {
# ホワイトリストにないオリジンは空文字列になり、ブラウザがブロックする
add_header ‘Access-Control-Allow-Origin’ $allow_origin always;
add_header ‘Access-Control-Allow-Methods’ ‘GET, POST, OPTIONS’ always;
add_header ‘Access-Control-Allow-Headers’ ‘DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization’ always;
add_header ‘Access-Control-Allow-Credentials’ ‘true’ always;

# プリフライトリクエストの処理(キャッシュして負荷軽減)
if ($request_method = ‘OPTIONS’) {
add_header ‘Access-Control-Allow-Origin’ $allow_origin always;
add_header ‘Access-Control-Max-Age’ 1728000;
add_header ‘Content-Type’ ‘text/plain; charset=utf-8’;
add_header ‘Content-Length’ 0;
return 204;
}
}
}

3. アプリケーション層(Python/FastAPI)での制御

Nginxの前段にプロキシがない環境や、動的にオリジンを判定したい場合は、バックエンドでしっかり実装する。PythonのFastAPIなら、`CORSMiddleware` を使うのが正攻法だ。

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

運用環境では環境変数から読み込むこと
ALLOWED_ORIGINS = [
“https://your-trusted-app.com”,
“https://admin.your-trusted-app.com”
]

app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS, # ワイルドカードは厳禁!
allow_credentials=True, # Cookieを使うなら必須
allow_methods=[“GET”, “POST”, “PUT”, “DELETE”],
allow_headers=[“Authorization”, “Content-Type”],
)

ここでのポイント: `allow_origins=[“”]` は絶対に書くな。コードレビューでこれを見つけたら、即座に修正依頼を出すこと。これがプロのコードレビューだ。

4. 最後に:インシデントを防ぐための「視点」

私が最も懸念しているのは、「動く」ことだけを目的とした開発だ。

CORS設定を適当に済ませるエンジニアは、多くの場合「なぜブラウザがそんな制約を設けているのか」という背景(同一生成元ポリシー:SOP)を理解していない。

  • 開発時のTips: ブラウザのデベロッパーツールで「Network」タブを開き、APIレスポンスの `Access-Control-Allow-Origin` が、自分の予想しているドメインと完全に一致しているかを確認する癖をつけよう。
  • WAFの活用: AWS WAFやCloudflareを利用しているなら、異常なOriginヘッダーを持つリクエストをブロックするルールを追加するのも一つの手だ。

セキュリティは「チェックリストを埋めること」ではなく、「攻撃者の思考回路を先読みして、土俵に上がらせないこと」だ。今回紹介した設定を明日からの開発に組み込み、胸を張れる堅牢なプロダクトを作ってほしい。

何か疑問があれば、いつでもコードを見せてくれ。現場の最前線で待っている。

コメント

タイトルとURLをコピーしました