CORSエラー「Access-Control-Allow-Origin cannot be * when credentials is true」の解消

CORS Security HTTP Backend
結論

Cookie通信時は Origin に * を指定できず、ホワイトリスト判定による動的出力が必須です。

// WHATWG Fetch Standard公式仕様:Express/Node.js での動的 CORS ヘッダー出力
import express from 'express';
const app = express();

const ALLOWED_ORIGINS = [
  'https://app.example.com',
  'https://admin.example.com',
];

app.use((req, res, next) => {
  const origin = req.headers.origin;

  // ⭕ 許可ドメインホワイトリストの判定
  if (origin && ALLOWED_ORIGINS.includes(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin); // ワイルドカード * は不可
    res.setHeader('Access-Control-Allow-Credentials', 'true');
  }

  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');

  if (req.method === 'OPTIONS') {
    return res.sendStatus(204); // プリフライトリクエストの即時返却
  }
  next();
});

WHATWG Fetch Standard公式仕様:CORSとCredentialsの基本法則

ブラウザ標準規格 WHATWG Fetch Standard(fetch.spec.whatwg.org)の規定通り、セキュリティ上の理由から以下の組み合わせはブラウザによって絶対拒絶されます。

Access-Control-Allow-Origin: * + Access-Control-Allow-Credentials: true => CORS Error Block

なぜ禁止されているのか?

もしこの組み合わせを許可してしまうと、悪意のある任意のサードパーティサイト(https://attacker.com)が、ユーザーのブラウザに保存された銀行や自社サービスの認証 Cookie を勝手に添付させてデータを取得する「セッションハイジャック脆弱性」が誰でも簡単に達成できてしまうためです。


実際に起こる障害:開発初期での * 設定による API 通信全滅事故

開発環境から本番環境へ移行する際、CORSエラーを安易に消そうとして Access-Control-Allow-Origin: *Access-Control-Allow-Credentials: true の両方をサーバーレスポンスヘッダーに記述してしまうケースが多発します。

その瞬間、ブラウザのコンソールに以下の赤文字エラーが吐き出されます:

Access to fetch at 'https://api.example.com/user' from origin 'https://app.example.com' 
has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header 
in the response must not be the wildcard '*' when the request's credentials mode is 'include'.

これにより、ユーザーのログイン処理やマイページ表示など、Cookie を伴う全 API リクエストがブラウザによって一斉遮断(TypeError: Failed to fetch)される大障害 が発生します。


解消手順

  1. クライアント側の fetch(url, { credentials: 'include' }) や Axios の withCredentials: true 設定を確認する
  2. サーバー側で Access-Control-Allow-Origin: *(ワイルドカード)の出力を即座に中止する
  3. リクエストヘッダーの Origin を読み取り、あらかじめ定義した ALLOWED_ORIGINS(ホワイトリスト配列)に含まれている場合のみ、その Origin 文字列(例: https://app.example.com)を動的にキャッチして送出する