TL;DR
- Cursor、Claude Code、Copilotはいずれも、新しいExpressおよびFastAPIプロジェクトで
Access-Control-Allow-Origin: *を一貫して生成する - ワイルドカードのCORSとCookie認証の組み合わせにより、インターネット上の任意のWebサイトからAPIがクロスオリジン攻撃にさらされる
- 修正:ワイルドカードを明示的なオリジン許可リストに置き換える -- だいたい2分
先週、友人のサイドプロジェクトをレビューしていました。Node/Expressのバックエンドで、Cursorが生成したようで見た目はきれいでした。ところが、ミドルウェア設定の中に埋もれているのを見つけました:
app.use(cors());
オプションなし。デフォルトのワイルドカードです。この1行が意味するのは、訪問者のブラウザから、どのWebサイトでも彼のAPIにリクエストできるということです。彼は翌週にユーザーアカウントを追加する予定でした。
これは単発ではありません。私は何十もの「雰囲気コード(vibe-coded)」のリポジトリを見てきました。このパターンは常に出てきます。AIは学習データの基準に照らせば間違ってはいません -- 引数なしでの cors() は、2019年のExpressチュートリアルのどれでも最初の結果です。AIは、そのコードが書かれた文脈を学ばずにパターンだけを学習してしまったのです。
脆弱なパターン(CWE-942)
Cursorに「ExpressアプリにCORSサポートを追加して」と頼むと、返ってくるのはだいたいこれです:
const cors = require('cors');
app.use(cors()); // Access-Control-Allow-Origin: *
FastAPIでも同様の扱いです:
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
allow_credentials=True と allow_origins=["*"] の組み合わせがごちゃごちゃしています。CORSの仕様では、資格情報が関係する場合に * を反映することは実際に禁止されています -- ブラウザはそれをブロックします。しかし、いくつかのCORSライブラリは、代わりに要求されたOriginヘッダを反映して応答することで、その保護を完全にすり抜けます。開発者が後からCORS設定を見直さずにセッションクッキーを追加すると、ワイルドカードは開発中は「まあ大丈夫」でしたが、そうではなくなります。
なぜこのパターンが残り続けるのか
学習コーパスには cors() とオプションなしがびっしりです。Stack Overflowの回答、公式のクイックスタートドキュメント、「10分でREST APIを作る」系の投稿。すべてがlocalhost向けに書かれており、ワイルドカードは無害です。
AIは「これは開発用であって本番用ではない」という注釈を学習していません。CORSサポートを追加するようプロンプトすると、AIは知っている中で最も一般的な例にそれを当てはめます。フロントエンドが特定のドメインで動いていることは知りません。認証をつなぐ予定だということも知りません。
修正は簡単です。問題は、プロンプトの中で誰もそれを求めないことです。
修正
Expressの場合は、ワイルドカードを明示的なリストに置き換えます:
const allowedOrigins = [
'https://yourapp.com',
'https://staging.yourapp.com',
process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : null
].filter(Boolean);
app.use(cors({
origin: (origin, callback) => {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true
}));
FastAPIの場合:
origins = [
"https://yourapp.com",
"https://staging.yourapp.com",
]
返却形式: {"translated": "翻訳されたHTML"}app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
)
環境変数から origins を取得します。1つの ALLOWED_ORIGINS 環境変数を、カンマ区切りで指定し、起動時に解析します。ステージングと本番は、コードに触れずに別々のままにできます。
もう2つ確認しておいてください。プリフライトの取り扱いと Access-Control-Allow-Methods です。AIが生成したコードは、カスタムミドルウェアでもそれを * に設定してしまいがちです。メソッドの明示的なリスト化には30秒かかります。
認証がない「本当に公開された」API(オープンなデータエンドポイント、公的なWebhookなど)を運用しているなら、ワイルドカードは問題なく正しい選択です。問題になるのは、ワイルドカードがユーザー固有のデータと認証と組み合わさったときに限られます。
私はこのために SafeWeave を使っています。これは Cursor と Claude Code に MCP サーバーとして組み込み、先に進む前に CORS の誤設定を指摘してくれます。とはいえ、semgrep と CORS ルールのある基本的な pre-commit hook でも、この投稿にあるほとんどは見つかるはずです。重要なのは、どんなツールを使うにせよ早い段階で見つけることです。




