CORS Misconfiguration: The Bypasses Auditors Miss
Cybersecurity
CORS bugs almost always boil down to four patterns: origin reflection, null origin, regex flaws, and the credentials trap. Most automated scanners catch one of the four. Manual review catches the rest.
By Arjun Raghavan, Security & Systems Lead, BIPI · September 10, 2024 · 7 min read
A trading platform asked us to validate their CORS posture before a B2B integration launch. Their staging environment had Access-Control-Allow-Origin set to whatever the request's Origin header was. With Access-Control-Allow-Credentials: true. The reasoning, per the engineer who wrote it: 'We need to support partner domains and we don't know all of them yet.'
Any logged-in user visiting an attacker-controlled site would have their session cookie auto-sent on a cross-origin request, the response readable by the attacker, and their entire portfolio history exfiltrated. The bug had been live for eleven months. No one flagged it because the API was 'internal only'.
Pattern one: origin reflection
The most common CORS bug. Server reads the Origin request header and echoes it into Access-Control-Allow-Origin. Combined with Access-Control-Allow-Credentials: true, this means any origin can read authenticated responses. Even without credentials, it's a bypass for IP-based access controls when the API is exposed to the internet but assumed to be 'browser-only from our domain'.
The fix is an explicit allowlist, not an algorithm. Hardcode the exact origins permitted, return them only on match, and return no ACAO header (or a single canonical origin) on mismatch. Anything that takes the request's Origin and decides whether to echo it back is a candidate for bypass.
Pattern two: null origin
Sandboxed iframes, file:// protocol pages, redirected requests, and certain data: URIs send Origin: null. Lots of CORS implementations treat null as a valid origin and allow it. An attacker can trigger a null-origin request by hosting a sandboxed iframe on any site they control.
If your CORS allowlist contains 'null' as a string, remove it. Today. There is no legitimate reason for production APIs to accept null origins with credentials, and the only legitimate uses (some Electron apps, certain mobile WebViews) should be addressed with a different mechanism.
Pattern three: regex flaws
Engineers writing 'flexible' CORS often build a regex like '^https://.*\.yourcompany\.com
- Unescaped dots: 'yourcompany.com' matches 'yourcompanyXcom'
- Missing end anchor: 'yourcompany.com' matches 'yourcompany.com.attacker.com'
- Missing start anchor: 'yourcompany.com' matches 'attackeryourcompany.com'
- Subdomain wildcard too broad: '*.yourcompany.com' allows attacker-controlled subdomains if takeover possible
Why testing CORS needs a real harness
Curl with arbitrary Origin headers shows you what the server returns, not what the browser will accept. Chrome and Firefox have subtle differences in how they handle null origins, file:// origins, and credentialed requests with non-credentialed responses. We use a Playwright-based harness that loads attack pages from controlled origins and asserts on whether the response is actually readable.
The harness lives in CI. Every API endpoint that handles authenticated state gets a CORS test that runs against staging. The test takes 200ms per endpoint. It catches regressions when someone adds a new wildcard to the allowlist 'temporarily for the demo' and forgets to remove it.
What good CORS looks like
Explicit allowlist of specific origins. Credentials enabled only on endpoints that need them. Vary: Origin set on every cached response with CORS headers. Preflight handled correctly with bounded max-age. ACAO never reflects the request, and never returns a wildcard alongside credentials.
The boring policy is the secure one. Every team that writes 'we need flexible CORS' is a team that's about to ship a session-theft vulnerability. The fix is an integration playbook that requires explicit origin registration with a security review, not a regex.
Read more field notes, explore our services, or get in touch at info@bipi.in. Privacy Policy · Terms.