TL;DR
- AIエディタは、ほぼすべてのExpress/Fastifyの雛形で、設定ゼロのままデフォルトで
cors()を使う - ワイルドカードCORSはブラウザの同一オリジン保護を無効化し、APIをあらゆるウェブサイトに公開してしまう
- 修正: 明示的なオリジン許可リストを定義し、反映する前にリクエストのOriginヘッダを検証する
先月、友人たちの3つのサイドプロジェクトをレビューしました。いずれもCursorを使ってバックエンドを足場(スキャフォールド)作成していました。そのうち2つは、本番でワイルドカードCORSが有効になっていました。開発だけではありません。本番環境で、実在のユーザーデータを提供していました。
コードはまったく問題なさそうに見えました。Cursorが生成したExpressのセットアップは綺麗でした。ルーティングは整理され、エラーハンドリングも堅実でした。けれども、すべてのapp.jsの先頭には同じ行がありました:
// CWE-942: Permissive Cross-domain Policy
app.use(cors()); // wildcard - every origin allowed
この1行、設定ゼロのままでも、ブラウザにこう伝えます。つまり、あなたのログイン中のユーザの代わりとして、このAPIに対して認証付きリクエストを行うことが、世界中のあらゆるウェブサイトに許されると。
ワイルドカードCORSが実際に意味すること
ブラウザはデフォルトで同一オリジンポリシーを強制します。evil.com のページは、APIがそのオリジンを明示的に許可しない限り、your-api.com へのfetchのレスポンスを読み取れません。
引数なしでcors()を呼び出すと、Expressはこのヘッダをすべてのレスポンスに追加します:
Access-Control-Allow-Origin: *
アスタリスクは「任意のオリジンからのリクエストを受け付ける」という意味です。ブラウザのブロックが止まります。すると、ユーザが開いているどのタブもあなたのAPIにアクセスしてレスポンスを読み取り、(ユーザが認証済みであることを前提に)データを持ち出せます。
これは、セッションクッキーやローカルストレージに保存されたJWTと組み合わせたときに最も危険です。悪意のあるページがこっそり読み込まれ、認証済みユーザとしてリクエストを送り、結果を読み取ります。誰もアラートを受け取りません。
なぜAIエディタはこれを出し続けるのか
このパターンは至るところにあります。チュートリアルはもちろん、Stack Overflowで最も投票数の多いExpressのCORS回答では、app.use(cors()) が「“すべてのオリジンを許可する”」というコメント付きで紹介されています。同じ行を含むMERNのボイラープレートはGitHub上のいたるところにあります。
このデータで学習したモデルは、「ExpressでCORSをセットアップする」ことを、引数なしのcors()と結び付けます。そうするとリクエストが動きます。開発者は先に進みます。許容的なCORSが原因でデモに失敗することはありません。悪用されるまで、このバグは静かに潜んだままです。
修正: 明示的な許可リスト
// CWE-942 mitigated
const ALLOWED_ORIGINS = [
'https://yourapp.com',
'https://staging.yourapp.com'
];
app.use(cors({
origin: function (origin, callback) {
// allow server-to-server requests (no Origin header)
if (!origin) return callback(null, true);
if (ALLOWED_ORIGINS.includes(origin)) return callback(null, true);
callback(new Error('Not allowed by CORS policy'));
},
credentials: true
}));
いくつか、特に注目すべき点があります:
!origin のチェックにより、サーバー間通信(curl、Postman、他のバックエンドサービス)を通過させられます。これにより、統合テストが壊れません。フロントエンドがクッキーやAuthorizationヘッダを送る場合は、credentials: true フラグが必要です。これがないと、ブラウザはクレデンシャルを黙って破棄します。
絶対に使ってはいけない組み合わせが1つあります。credentials: true と origin: '*' の組み合わせです。ブラウザは、ワイルドカードオリジンに対してクレデンシャルを送信することを拒否します。この設定があると、認証が黙って壊れているのに、CORSは依然として大幅にオープンのままです。1つの設定ミスで、2つのバグが手に入るというわけです。
いま確認すべきこと
app.js または server.ts を開いて cors( を検索してください。どこかに空の括弧(空のパーレン)があれば、この問題が起きています。
あわせて確認してください:
- レスポンスに実際に含まれている
Access-Control-Allow-Originヘッダ(開発者ツールのNetworkタブ) - セッションミドルウェアやCookie認証の背後にあるルート(重要なのはこれらです)
- あなたのAPIゲートウェイまたはCDNのCORS設定 - アプリレベルの設定を、こっそり上書きしてしまう可能性があります
私はこのためにSafeWeaveを運用しています。これはCursorとClaude Codeに、MCPサーバとしてフックし、先に進む前にこれらのパターンをフラグ付けします。それでも、semgrepとgitleaksを使った基本的なpre-commitフックでも、この投稿に含まれる内容のほとんどは検知できます。重要なのは、どのツールを使うにせよ、早い段階で検知することです。




