VNX-SEC-012 – CORS Wildcard or Origin Reflection
Overview
This rule reports three shapes: the Access-Control-Allow-Origin header set to the request’s own Origin header (directly or through a variable read from it a few lines above) with no allowlist check on the origin; the header set to * (w.Header().Set, response[...] =, PHP header(), nginx add_header, Apache Header set, the ingress-nginx cors-allow-origin annotation); and CORS middleware configured for every origin (AWS CDK allowOrigins: Cors.ALL_ORIGINS, FastAPI allow_origins=["*"], Flask-CORS CORS(app), django-cors-headers CORS_ALLOW_ALL_ORIGINS = True, gin AllowAllOrigins, rs/cors AllowOriginFunc returning true, Spring allowedOriginPatterns("*") and bare @CrossOrigin, ASP.NET Core AllowAnyOrigin() and SetIsOriginAllowed(_ => true), Laravel and rack-cors wildcards). It does not report a wildcard whose configuration explicitly disables credentials nearby, specific origins, an origin checked against an allowlist, the header sent by client code (fetch/axios request headers), assertions, tests and documentation. CORS in JavaScript and TypeScript servers (res.setHeader, the cors package, Fastify and NestJS) is reported by VNX-942, so one problem is not reported twice; for those files this rule reports only the AWS CDK construct. CORS controls which websites can read responses from your API using cross-origin JavaScript. Setting it too broadly means any malicious website can make authenticated requests to your API on behalf of a logged-in user and read the responses.
Severity: High | CWE: CWE-942 – Permissive Cross-domain Policy with Untrusted Domains
Why This Matters
There is an important distinction between two types of permissive CORS:
Wildcard (*): This allows any origin to make cross-origin requests, but browsers will not send cookies or Authorization headers with wildcards. This is appropriate for truly public APIs (like a CDN or public data endpoint) but dangerous for any API that uses cookies or tokens for authentication.
Origin reflection (origin: true in Express, or echoing Origin back as Access-Control-Allow-Origin): This is the more dangerous pattern. It appears to be dynamic, but effectively allows any origin — and crucially, it can be combined with Access-Control-Allow-Credentials: true. With credentials enabled and an overly permissive origin, any website can make credentialed cross-origin requests to your API, read the responses, and exfiltrate data on behalf of your logged-in users.
The CORS misconfiguration in the 2018 British Airways breach (MITRE ATT&CK T1189) exemplifies how attackers exploit this to steal session tokens and payment data.
What Gets Flagged
# FLAGGED: FastAPI wildcard CORS
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True, # dangerous combined with wildcard
allow_methods=["*"],
)
// FLAGGED: AWS CDK API Gateway preflight for every origin
defaultCorsPreflightOptions: {
allowOrigins: apigw.Cors.ALL_ORIGINS,
allowCredentials: true,
},
Express, Fastify and NestJS servers (res.setHeader('Access-Control-Allow-Origin', req.headers.origin), app.use(cors()), cors({ origin: true })) are reported by VNX-942.
// FLAGGED
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
r.Use(cors.New(cors.Config{AllowAllOrigins: true, AllowCredentials: true}))
# FLAGGED
add_header Access-Control-Allow-Origin $http_origin always;
Not flagged: if (ALLOWED.has(origin)) res.setHeader('Access-Control-Allow-Origin', origin), allow_origins=["https://app.example.com"], and allow_origins=["*"] beside allow_credentials=False.
Remediation
- Replace the wildcard or reflection with an explicit allowlist of trusted origins:
# SAFE: explicit allowlist of trusted origins (FastAPI)
from fastapi.middleware.cors import CORSMiddleware
ALLOWED_ORIGINS = [
"https://app.example.com",
"https://admin.example.com",
]
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["GET", "POST"],
allow_headers=["Authorization", "Content-Type"],
)
// SAFE: validate origin against allowlist in Express
const ALLOWED_ORIGINS = new Set([
'https://app.example.com',
'https://admin.example.com',
]);
app.use(cors({
origin: (origin, callback) => {
if (!origin || ALLOWED_ORIGINS.has(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
}));
Never combine
Access-Control-Allow-Credentials: truewith a wildcard or reflected origin. Browsers will block this combination in modern versions, but older browsers may not, and the coding pattern itself signals a CORS misconfiguration.For truly public APIs (no authentication, no sensitive data), a wildcard without credentials is acceptable:
# SAFE: public read-only API — wildcard without credentials is OK
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=False, # explicitly no credentials
)
Review preflight handling. Ensure that
OPTIONSpreflight requests are handled correctly and thatAccess-Control-Max-Ageis set to cache preflight results and reduce latency.Test your CORS policy using browser devtools or tools like
curl -H "Origin: https://evil.com" -vto verify that unauthorized origins receive an appropriate rejection.