CORS: never pair a wildcard with credentials
Bind credentials to a named origin instead of a wildcard so no other site can make authenticated reads
The threat
Two things showed up together on your API: Access-Control-Allow-Origin: * and Access-Control-Allow-Credentials: true. On their own, each is ordinary. Together they try to tell the browser βany website may send this userβs cookies to my API and read what comes back,β which is the Same-Origin Policy, the rule that normally stops one site from reading another, switched off for your API.
In practice browsers refuse the literal * when credentials are involved, so code that wants this behavior usually reflects the callerβs origin instead and ends up in the same place: any site a logged-in visitor opens can quietly make authenticated calls to your API and read their data back. That is a direct data-theft path, which is why this rates higher than a plain reflected origin.
The exact fix
Two moves, and you almost always want the first.
1. Decide whether you need credentials cross-origin at all. If your frontend and API are same-origin, or you authenticate with a token in an Authorization header rather than cookies, drop credentials entirely. For public, non-sensitive endpoints a plain wildcard is then fine:
Access-Control-Allow-Origin: *
# and NO Access-Control-Allow-Credentials header at all
2. If you genuinely need cookies cross-origin, replace * with a server-side allowlist and echo only a matching origin, never the wildcard.
Express:
const cors = require('cors');
const allowlist = new Set(['https://app.yourdomain.com']);
app.use(cors({
origin(origin, cb) {
if (!origin || allowlist.has(origin)) return cb(null, true);
return cb(null, false);
},
credentials: true, // now paired with a specific origin, never '*'
}));
nginx, gate the credentialed header behind an allowlist match:
# in the http { } block
map $http_origin $cors_ok {
default "";
"https://app.yourdomain.com" $http_origin;
}
location /api/ {
add_header Access-Control-Allow-Origin $cors_ok always;
add_header Access-Control-Allow-Credentials true always;
add_header Vary Origin always;
}
The one rule to remember:
Access-Control-Allow-Credentials: truemay only ever appear next to a single, specific origin you chose, never*and never a blindly reflected one. If you do not need credentials, do not send that header, and a wildcard stays harmless.
Verify it
curl -sI -H "Origin: https://evil.example.com" https://yourdomain.com/api/ | grep -i access-control
You should never see Access-Control-Allow-Origin: * and Access-Control-Allow-Credentials: true in the same response, and a stranger origin like the one above must not be echoed back. A credentialed endpoint should return only an origin you put on the list, or no CORS headers at all.
Proof
The ApeCyber scanner flags this exact pairing passively, from the outside, reading only the response headers your server already sends, touching nothing. Bind credentials to a single named origin (or drop them, so a plain wildcard stays harmless) and the finding clears, a clean β20 off your posture score.