CORS allows arbitrary origins with credentials

The server reflects any Origin in Access-Control-Allow-Origin while also sending Access-Control-Allow-Credentials: True. Any website can read authenticated responses from your site in a visitor's browser.

Do this: Never pair a wildcard origin with credentials; list exact origins. Reflecting any origin with credentials lets any site read your authenticated responses using the visitor's session.
PassArbitrary origins are not granted credentialed cross-origin access.
HighArbitrary origins are reflected in Access-Control-Allow-Origin with credentials allowed.

The fix, in one snippet

Example to adapt Allow an exact list, never a wildcard with credentials
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Vary: Origin

Illustrative values. Change the paths, hostnames and options to match your own site before using it.

The sections below explain what is tested, why it matters and the alternatives.

What we test

Scan.now sends two ordinary GET / requests over HTTPS with an Origin request header: one using an origin that cannot be yours (https://scan-now-probe.invalid) and one using the literal value null. It then reads Access-Control-Allow-Origin and Access-Control-Allow-Credentials from each response. The check fails at high severity when the arbitrary origin, or null, is echoed back and credentials are allowed. It reports a lower-severity note when the origin is reflected without the credentials flag, and when Access-Control-Allow-Origin: * is used, which browsers never combine with credentials but which still exposes any response that is protected by network position rather than cookies. Only the homepage is probed; API routes can have their own, different, CORS configuration, so treat a pass as a sample rather than a guarantee. No preflight or custom methods are sent.

Why it matters

The same-origin policy is what stops evil.example from reading your users' data out of your site. CORS is the mechanism for deliberately relaxing it. A server that copies whatever Origin it receives into the allow header, and adds Allow-Credentials: true, has relaxed it for everyone: any page the victim visits can issue fetch('https://your-site/account', {credentials: 'include'}) and read the response, including personal data, API keys shown in a dashboard, or the anti-CSRF token needed to make state-changing requests. The null origin is exploitable through a sandboxed iframe, which is why it is treated as equivalent. Reflecting subdomains by suffix match is a common near-miss: a single XSS or takeover on any subdomain then yields the same access. James Kettle's 2016 PortSwigger research documented these patterns on major sites and they remain a staple of bug-bounty reports. Background on the policy is in the website security hub.

How to fix it

Compare the incoming Origin against an explicit allowlist and echo it only on a match; never derive the value from the request by pattern. Express with the cors package:

const allowed = ['https://app.example.com', 'https://admin.example.com'];
app.use(cors({
  origin: (origin, cb) => cb(null, !origin || allowed.includes(origin)),
  credentials: true,
}));

nginx, using a map so unmatched origins get no header at all:

map $http_origin $cors_origin {
    default "";
    "https://app.example.com"   $http_origin;
    "https://admin.example.com" $http_origin;
}
server {
    add_header Access-Control-Allow-Origin $cors_origin always;
    add_header Access-Control-Allow-Credentials "true" always;
    add_header Vary "Origin" always;
}

Apache with mod_headers uses SetEnvIf Origin plus Header set Access-Control-Allow-Origin "%{ORIGIN}e" env=ORIGIN against a regex of the exact allowed hosts. Always add Vary: Origin so caches do not serve one origin's allow header to another. If the endpoint is public and needs no cookies, use * without the credentials flag. Cloudflare Transform Rules can add fixed CORS headers, but dynamic allowlists belong in the application.

Where this fits

CORS allows arbitrary origins with credentials is check 1 of 13 that the website vulnerability scanner runs under http security headers, ordered the way they are worth fixing. That ordering is the point: Fixing this one while the check above it still fails buys less than it looks like.

What fixing this still leaves open

CORS allows arbitrary origins with credentials closes one route in. Immediately below it: Clickjacking protection (X-Frame-Options / frame-ancestors), where the page can be embedded in a frame on any other site; Content-Security-Policy is weak, where a Content-Security-Policy exists but contains directives that let injected script run anyway: 'unsafe-inline' without nonces, 'unsafe-eval', wildcards, data: URIs, or missing object-src and base-uri; Content-Security-Policy missing, where the page is served without an enforced Content-Security-Policy.

Found in the same scan

The website vulnerability scanner reports this alongside checks from other categories that are at least as serious, including Certificate chain and hostname validation, where the certificate presented for this hostname did not validate: The chain does not reach a trusted root, an intermediate is missing, the name does not match, or the certificate is self-signed or expired, and Exposed .env configuration file, where a .env configuration file is served from the web root. A single run of website vulnerability scanner answers all of them at once.

Prompt for an AI Hand this check to an assistant Sign in to copy it
The first few lines
You are a senior web engineer. I ran a security and SEO scanner against my site and it reported the finding below. Fix it properly rather than suppressing the symptom.

Finding: CORS allows arbitrary origins with credentials (high severity)
Scanner check id: cors-misconfiguration
19 more lines, including the evidence and the exact fix

The rest of this prompt names the pages and line numbers we found the problem on, the configuration to change, and the constraints a good answer has to respect. It is free, it just needs an account so the work is not scraped wholesale.

Sign in with Google

Signing in is free and takes one click. We store your email address and nothing else.

References

  1. MDN: Cross-Origin Resource Sharing (CORS)
  2. PortSwigger Research: Exploiting CORS misconfigurations
  3. Fetch Standard: CORS protocol

Related guides