Fix CORS Misconfiguration: Wildcard Origins, Credentials, Null Origin, Vary Header

fix now global

CORS controls which origins can read responses from cross-origin requests. Misconfigurations fall into four patterns: wildcard origin with credentials enabled, unrestricted wildcard origin, null origin allowed, and missing Vary: Origin header. Each weakens isolation differently. Wildcard with credentials is the most severe because it permits credential theft from any origin. The others create data exposure, sandbox bypass, and cache poisoning conditions respectively.

What's wrong

The server's CORS headers permit cross-origin access in ways the same-origin policy was designed to prevent. Four distinct patterns appear in practice. Wildcard origin with credentials: Access-Control-Allow-Origin is set to * while Access-Control-Allow-Credentials is true. Browsers reject this combination, but servers that reflect the requesting Origin header while also setting credentials: true produce the same effect without the wildcard. Any origin can make credentialed requests and read responses containing session-specific data. Wildcard origin: Access-Control-Allow-Origin: * without credentials. Any page on any domain can read the response body. Public APIs may intend this, but most endpoints do not. Internal data, error messages, and response structures leak to any origin. Null origin allowed: Access-Control-Allow-Origin: null appears in responses to requests from sandboxed iframes, data: URIs, and local files. Servers that allowlist null trust a class of contexts specifically designed to be untrusted. An attacker creates a sandboxed iframe whose origin is null and makes authenticated requests that the server accepts. Missing Vary: Origin: When the server returns different Access-Control-Allow-Origin values based on the request Origin, responses must include Vary: Origin. Without it, CDNs and proxies cache a response tied to one origin and serve it to another. A cached response with Access-Control-Allow-Origin: attacker.com reaches legitimate callers, or a cached response for the legitimate origin reaches attackers, depending on request ordering.

Why it matters

CORS misconfigurations bypass the same-origin policy, which is the primary isolation boundary in browsers. The damage depends on the pattern. Wildcard with credentials enables full credential theft. An attacker's page makes a fetch with credentials: 'include' to the vulnerable endpoint. The browser attaches cookies and auth headers. The server responds with the user's data and a permissive CORS header. The attacker reads the response. Session tokens, personal data, and API keys in the response body are exfiltrated. This is functionally equivalent to a universal XSS on the target domain. Wildcard origin without credentials exposes response data to any origin. Endpoints that return internal configuration, user counts, pricing tiers, or undocumented API structures become public. The data is not authenticated, but it was never intended to be world-readable. Null origin creates a sandbox bypass. Sandboxed contexts exist precisely because they should not be trusted. Allowing null origin grants full access to any sandboxed iframe, which attackers create trivially. The attack requires no user interaction beyond visiting a page. Missing Vary: Origin causes cache poisoning. A CDN caches a response with Access-Control-Allow-Origin set to one origin and serves it to requests from other origins. Legitimate cross-origin consumers receive responses with incorrect CORS headers and fail. Or attackers receive responses with headers permitting their origin and succeed. The result depends on which request hits the cache first.

The correct change

Replace permissive CORS configurations with explicit origin validation. For credentialed endpoints: Maintain an allowlist of trusted origins. Check the incoming Origin header against the list. If the origin is allowed, reflect it in Access-Control-Allow-Origin and set Access-Control-Allow-Credentials: true. If the origin is not recognized, omit CORS headers entirely or return a default non-matching value. Never use a wildcard with credentials. For public endpoints: Access-Control-Allow-Origin: * is acceptable only when the endpoint genuinely serves public data, requires no authentication, and contains no internal information. Remove credentials: true from public endpoints. For null origin: Remove null from any origin allowlist. No legitimate production scenario requires trusting the null origin. If sandbox contexts need API access, route them through a backend proxy that authenticates via a mechanism other than browser-managed cookies. For Vary header: Add Vary: Origin to every response where Access-Control-Allow-Origin varies by request. This tells caches to store separate copies per origin. If the server always returns the same CORS headers regardless of origin, Vary: Origin is unnecessary but harmless. Preflight handling: Ensure OPTIONS responses for preflighted requests return correct Access-Control-Allow-Methods, Access-Control-Allow-Headers, and Access-Control-Max-Age values. Preflight responses must also include the validated Access-Control-Allow-Origin.

Scope

This condition applies globally. CORS headers are set per-response, but misconfigured origin validation typically affects all endpoints behind a shared middleware, reverse proxy, or CDN rule. A single permissive CORS configuration in a load balancer or API gateway propagates to every route it handles.

How to verify

  • · Validation confirms the condition is resolved:
  • · • Send a cross-origin request with Origin: https://attacker.example — response must not reflect that origin in Access-Control-Allow-Origin
  • · • Send a credentialed request with a spoofed origin — response must not include Access-Control-Allow-Credentials: true for untrusted origins
  • · • Send a request with Origin: null — response must not include Access-Control-Allow-Origin: null
  • · • Responses that vary Access-Control-Allow-Origin by request include Vary: Origin
  • · • OPTIONS preflight requests return correct methods, headers, and validated origin
  • · • CDN-cached responses include correct CORS headers for each distinct origin
  • · • Legitimate cross-origin consumers still function with the validated configuration

Takeaway

  • · CORS origin validation must use an explicit allowlist, never a wildcard with credentials
  • · Reflecting the request Origin header without validation is equivalent to allowing all origins
  • · Null origin is not a safe default — it is the origin of untrusted sandbox contexts
  • · Missing Vary: Origin causes cache poisoning when CORS headers change per request

FAQ

Why do browsers reject Access-Control-Allow-Origin: * with credentials but the vulnerability still exists?

Browsers block the literal wildcard when credentials are included. But most vulnerable servers do not send the literal wildcard. They read the Origin header from the request and reflect it in the response while also setting Access-Control-Allow-Credentials: true. The browser sees a specific matching origin, not a wildcard, and permits the response. The server trusts every origin that asks.

Is Access-Control-Allow-Origin: * safe for public APIs?

Yes, when three conditions hold: the endpoint requires no authentication, the response contains no internal or sensitive information, and credentials are not enabled. Public CDN resources, open datasets, and unauthenticated status endpoints can use a wildcard. If any of these conditions fail, use explicit origin validation instead.

How does an attacker exploit null origin?

The attacker creates an HTML page containing a sandboxed iframe with allow-scripts. Code inside the sandbox executes with origin null. It makes a fetch request to the target with credentials included. If the server allows null origin, the response is readable inside the sandbox. The outer page communicates with the sandbox via postMessage to exfiltrate the data. The entire attack runs on a page the victim visits.

What happens if Vary: Origin is missing and no CDN is in use?

Direct browser-to-server requests are unaffected because there is no shared cache. The risk appears when any cache sits between client and server: CDNs, reverse proxies, load balancer caches, or service mesh caching. If the infrastructure has no shared cache and will never add one, the missing header causes no issue. In practice, caching layers change without coordinating with CORS configuration.

Can CORS misconfiguration be exploited without user interaction?

It requires the victim to visit an attacker-controlled page, which triggers the cross-origin request automatically. No clicks, form submissions, or other interaction is needed beyond the initial page load. The fetch executes on load, reads the response, and exfiltrates data. Phishing, ad injection, or compromised third-party scripts can deliver the attacker's page.

Should origin validation use a regex or an exact-match list?

Exact-match list. Regex-based origin validation is a frequent source of bypasses. Patterns like /example\.com$/ match evil-example.com. Anchoring and escaping errors create bypass paths. An explicit list of full origins — https://app.example.com, https://dashboard.example.com — eliminates regex bypass risks. If subdomain patterns are needed, validate the suffix after splitting on dots, not with a regex.

The fix

Intent: Prevent cross-origin data theft and cache poisoning by enforcing explicit origin validation in CORS headers

End state: CORS headers reflect only allowlisted origins, credentials are never paired with wildcards, null origin is rejected, and Vary: Origin is present when CORS headers vary by request

  • Do not reflect the request Origin header without validating it against an allowlist
  • Do not use Access-Control-Allow-Origin: * on endpoints that set Access-Control-Allow-Credentials: true
  • Do not include null in the origin allowlist
  • Do not omit Vary: Origin when the Access-Control-Allow-Origin value depends on the request