Wildcard CORS: Access-Control-Allow-Origin * in your AI-generated backend
Symptoms
- Your API responds with Access-Control-Allow-Origin: * on every request
- Or worse, it reflects whatever Origin header it receives, often with credentials enabled
- Any web page a logged-in user visits (a phishing site, a malicious ad iframe) can silently call your API from their browser and read the responses
- Security scanners flag it as CWE-942, permissive cross-domain policy
Why it happens
Assistants scaffold the tutorial pattern
Cursor, Copilot, and Claude Code overwhelmingly generate Express or FastAPI backends with app.use(cors()) and no options, or allow_origins=["*"], because the training corpus is saturated with localhost quick-starts where a wildcard is harmless. The dev-only caveat never made it into the pattern.
Origin reflection is the nastier variant
The CORS spec forbids * combined with credentials, so some libraries 'helpfully' reflect the requesting Origin instead. That re-enables credentialed cross-origin access and defeats the spec's protection entirely.
How to fix it
- Replace the wildcard with an explicit allowlistRead allowed origins from an environment variable (for example ALLOWED_ORIGINS, comma-separated, parsed at startup) so dev, staging, and prod differ without code changes.
- Never mix credentials with wildcard or reflectionDo not combine credentials: true (or allow_credentials=True) with a wildcard or with origin reflection. If you need cookies cross-origin, enumerate exact origins.
- Audit what the server actually sendsRun `curl -s -I -H 'Origin: https://evil.example' https://yourapi.com/endpoint` and check whether Access-Control-Allow-Origin echoes the attacker origin. Test the deployed server, not just your local copy.
- Keep enforcing auth regardless of originCORS is not authentication; it only constrains browsers. curl and server-side attackers ignore it entirely, so token or session checks stay mandatory on every request.
- Make the fix stick with a scanner ruleAdd a Semgrep rule (or a scanner hook) to pre-commit and CI that flags cors() with no options and allow_origins=["*"]. Assistants will keep regenerating the pattern; the rule catches it every time.
How to prevent it
- Scaffold new backends with the allowlist pattern so the AI has a correct example to extend
- Keep a Semgrep rule for bare cors() and allow_origins=["*"] in pre-commit and CI
- Store allowed origins in an environment variable per environment
- Re-run the curl origin-echo check after every AI edit to server config
Tools that actually fix this
Recommended because they address the failure mode above, not because of the payout. Some are affiliate links; see how we choose.
Snyk Code catches injection and XSS right in your editor, and Snyk Open Source flags vulnerable and malicious packages. It covers several AI-code failure modes at once and is free for open source.
Try Snyk →All-in-one appsec (code scanning, dependencies, secret detection, containers) with low-noise output, built for small teams that cannot run five separate tools. One dashboard covers several failure modes.
Try Aikido Security →Frequently asked questions
Is Access-Control-Allow-Origin: * a security risk?
Access-Control-Allow-Origin: * is a risk whenever your API serves anything private: it lets any web page make requests to your API from a visitor's browser and read the responses. Scanners classify it as CWE-942, permissive cross-domain policy. The spec at least blocks * with credentials, but libraries that reflect the Origin header instead re-enable credentialed access and are strictly worse.
Is cors() with no options safe in production Express?
No. app.use(cors()) with no options allows every origin, which is the wildcard pattern AI assistants scaffold from localhost tutorials. In production, pass an explicit origin allowlist read from an environment variable, and never enable credentials alongside a wildcard or origin reflection.
Does CORS protect my API from attackers?
No. CORS only constrains browsers; curl, scripts, and server-side attackers ignore it completely. It decides which web pages can read cross-origin responses in a browser, nothing more, so you must keep enforcing token or session authentication on every request regardless of origin.
Why do AI assistants keep generating wildcard CORS?
Because the training corpus is saturated with localhost tutorials and quick-starts where a wildcard is harmless, assistants like Cursor, Copilot, and Claude Code scaffold app.use(cors()) or allow_origins=["*"] by default. The dev-only caveat never made it into the learned pattern, so the durable fix lives in your CI: a Semgrep rule that flags the pattern every time it reappears.
Sources
- AI-generated APIs keep shipping wildcard CORS: here's the fix
- Your AI wrote a CORS config that lets any website read your API
- Sourcery: API CORS misconfiguration
- bswen: AI CORS wildcard in production security
Related errors
No auth on your AI-generated API endpoints: anyone can call your backend
SQL injection and XSS in AI-generated code
The AI put your API key in the frontend: OpenAI, Stripe, or Supabase keys visible in the JS bundle
Supabase Row Level Security is off: your whole database is readable with the anon key
The Vibe Oops briefing
One email when something ships to production that should not have. New incidents, new error guides, no filler.