app.enableCors({
origin: (origin, cb) => cb(null, origin),
credentials: true,
});
The problem: it sends back every Origin together with Access-Control-Allow-Credentials: true. This is the same as allowing * with credentials, which the spec forbids for good reason. Any site that a logged-in user visits can call the API with the user's cookies and read the responses. So another site can read private data.
The fix is an explicit allowlist. Compare the incoming origin with exact strings like https://app.shop.com. Echo it back only if it matches.
Avoid these mistakes:
- Loose checks like
endsWith("shop.com"). They also matchevilshop.com. - Allowing the
nullorigin. Sandboxed iframes and some redirects sendnull.
When the allowed origin changes per request, send Vary: Origin. This stops caches from serving one origin's CORS headers to another origin. Setting Access-Control-Max-Age reduces repeated preflights. A preflight is the OPTIONS request the browser sends before some cross-origin requests.
- Sending back any origin with credentials exposes user data to other sites.
- Use an exact allowlist of origins.
- Avoid suffix or regex mistakes, and don't allow the
nullorigin. - Send
Vary: Originwhen the allowed origin changes per request.