Join our Newsletter — 33% off our NHI Course
Home› Guides› CORS Misconfiguration Guide: Origin Policies That Do Not…
Guide Web & API Security

CORS Misconfiguration Guide: Origin Policies That Do Not Leak Data

PDF for newsletter subscribers · free to read online

← All guides
By Lalit Choda, NHI Mgmt Group Updated 24 October 2026 13 min read
On this page

A misconfigured Cross-Origin Resource Sharing (CORS) policy lets a website the attacker controls read responses from your application using the victim's own browser session. CORS exists to relax the browser's same-origin policy in a controlled way, so every header it adds is a grant of trust, and a careless grant turns an authenticated API into a data source for any site the victim visits. The flaws are rarely exotic: an origin echoed back without checking, an unanchored regular expression, the null origin on an allowlist, or a framework default left on. This guide explains how the browser applies CORS, where misconfigurations appear in common frameworks, how to detect them and how to build an origin policy that holds up. It is written for application security engineers, developers and testers who own web applications and APIs.

Key takeaways

  • CORS loosens security, it does not add it. The browser enforces it, so it never protects an endpoint from scripts, servers or tools outside a browser; authentication and authorisation still have to be enforced on the server.
  • Credentialed CORS is the high-risk case. Any origin allowed together with Access-Control-Allow-Credentials: true can read whatever the logged-in user can read.
  • Reflecting the Origin header is the most damaging mistake. Echoing the request's Origin into Access-Control-Allow-Origin with credentials allowed is equivalent to trusting every website on the internet.
  • Match origins exactly. Compare the full scheme, host and port against a fixed allowlist; avoid substring, prefix or unanchored pattern checks, and never allowlist null.
  • A trusted origin inherits your risk. Every allowed origin, including subdomains and HTTP origins, becomes part of your attack surface: an XSS flaw or takeover there exposes your API.

How the attack works

Browsers send cookies and HTTP authentication with many cross-origin requests, but by default they do not let the calling page read the response. CORS is the mechanism a server uses to tell the browser "this other origin may read my response". An origin is the combination of scheme, host and port, so https://app.example.com and http://app.example.com are different origins.

Simple and preflighted requests

  • Simple requests: GET, HEAD or POST using only CORS-safelisted request headers (such as Accept, Accept-Language, Content-Language and Content-Type limited to application/x-www-form-urlencoded, multipart/form-data or text/plain). The browser sends the request straight away and decides afterwards whether script may read the response.
  • Preflighted requests: anything else (PUT, DELETE, JSON bodies, custom headers) triggers an OPTIONS request carrying Access-Control-Request-Method and Access-Control-Request-Headers. Preflights never carry credentials. The server answers with Access-Control-Allow-Methods, Access-Control-Allow-Headers and optionally Access-Control-Max-Age.
  • Credentialed requests: the page must opt in (credentials: "include" in fetch() or withCredentials in XMLHttpRequest) and the response must carry Access-Control-Allow-Credentials: true plus an explicit origin. Browsers refuse a credentialed response that uses *.

The abuse pattern

The attacker lures a signed-in user to a page they control. Script on that page sends a credentialed request to the target API. If the API's response names the attacker's origin (or reflects whatever it receives) and allows credentials, the browser hands the response body to the attacker's script, which can exfiltrate account data, API keys shown in a settings page or anti-CSRF tokens that unlock further actions. Because the request comes from the victim's browser, it also passes IP allowlists and VPN-only restrictions. The browser rule against * with credentials is why vulnerable servers usually reflect origins: developers who hit that error often "fix" it by echoing the request origin, which removes the protection entirely.

A related variant needs no credentials at all. A permissive policy on an internal service that relies on network position rather than authentication lets a public website read intranet responses through any employee's browser.

Where it shows up

Most CORS flaws come from framework configuration rather than hand-written headers. The table lists the settings that matter in widely used stacks, taken from each project's documentation.

Framework or libraryRisky settingSafer setting
ASP.NET Core CORS policyAllowAnyOrigin(); SetIsOriginAllowed(_ => true) with AllowCredentials(), which Microsoft documents as bypassing built-in checksNamed policy with WithOrigins(...) (no trailing slash), WithMethods/WithHeaders, applied with [EnableCors("Policy")] or RequireCors
Express cors middlewareDefault origin: "*"; origin: true, which reflects the request origin; unanchored RegExpArray of exact origin strings, or a function that checks an allowlist; credentials: true only where needed
Spring MVC @CrossOrigin and global configAnnotation defaults allow all origins; broad allowedOriginPatterns with allowCredentials(true)Explicit allowedOrigins per mapping; Spring Security's built-in CORS support rather than a separate filter
Rack::Cors (Ruby, Rails)origins '*' on sensitive resources; regex without \A and \z anchorsExact origins in the allow block, resource rules scoped to the paths that need them
FastAPI / Starlette CORSMiddlewareLoose allow_origin_regex; allow_methods=["*"] and allow_headers=["*"] by habitExplicit allow_origins, methods and headers; the documentation states none may be ["*"] when allow_credentials=True
Django (django-cors-headers, the package the Django REST framework docs name)Allow-all settings copied from developmentExplicit origin allowlist kept per environment
API gateways, CDNs and reverse proxiesHeader injected at the edge for every path, independent of the application's own policyOne owner for CORS headers; edge rules limited to named paths and origins

Typical misconfiguration patterns

  • Origin reflection: code or a library setting copies Origin straight into Access-Control-Allow-Origin.
  • Weak matching: checks that a value "contains", "starts with" or "ends with" a domain. OWASP and the Rack::Cors maintainers both warn that unanchored patterns match attacker-registered lookalike domains.
  • Trusting null: the null origin is sent from sandboxed iframes and some redirect and file contexts, so allowlisting it (often to make local testing work) lets attacker content qualify. OWASP WSTG treats it as an exploitable wildcard.
  • Wildcard subdomains: trusting *.example.com trusts every marketing microsite, legacy host and dangling DNS record under it.
  • HTTP origins: allowing http:// versions of trusted hosts lets anyone able to tamper with network traffic inject script into the trusted origin.
  • Missing Vary: Origin: a dynamically chosen origin without Vary: Origin lets a shared cache serve one origin's grant to another.
  • Policy applied to the whole site: the OWASP HTML5 Security Cheat Sheet advises setting CORS headers only on the URLs that need cross-origin access, not for the whole domain.

Impact

ConfigurationWhat an attacker's page can doSeverity
Reflected or arbitrary origin plus credentialsRead any response the signed-in user can see, including personal data, tokens and CSRF tokensHigh to critical
null or weakly matched origin plus credentialsSame as above, with a little more set-upHigh
Trusted subdomain or HTTP origin plus credentialsRead data once the trusted origin is compromised through XSS, takeover or traffic tamperingMedium to high
* without credentials on internal or IP-restricted servicesRead intranet data through an employee's browserMedium to high
* without credentials on genuinely public dataNothing beyond what anyone can fetch directlyLow or none

MITRE classifies the weakness as CWE-942, Permissive Cross-domain Security Policy with Untrusted Domains, whose listed consequences include bypassing the same-origin policy and reading application data. Reading JSON across origins is also the goal of CAPEC-111 (JSON Hijacking), which CORS misconfiguration makes trivial. Data exposed this way often includes API keys and personal access tokens displayed in account pages, which turns a browser flaw into a non-human identity exposure that outlives the session. See the API Security Guide.

Detection

Testing

  • Origin probing: OWASP WSTG test WSTG-CLNT-07 sends requests with a range of Origin values (an unrelated domain, a lookalike suffix or prefix of your domain, null, an HTTP version of a trusted host) and records the Access-Control-Allow-Origin and Access-Control-Allow-Credentials values returned. Any echo of an untrusted value is a finding.
  • Preflight review: inspect OPTIONS responses for overly broad Access-Control-Allow-Methods, Access-Control-Allow-Headers and long Access-Control-Max-Age values. Remember that command-line tools ignore CORS, so they show what the server sends, not what the browser would allow.
  • Code and configuration search: grep repositories and infrastructure code for AllowAnyOrigin, SetIsOriginAllowed, origin: true, @CrossOrigin without arguments, origins '*', allow_origin_regex and hand-written Access-Control-Allow-Origin headers. Add these as static analysis or linting rules so they fail the build.
  • DAST coverage: include authenticated CORS checks in dynamic scans, because the dangerous combinations only appear on endpoints that need a session. See the Web Application Security Testing Guide.

Monitoring

  • Log the Origin header and the CORS decision (allowed or refused) for sensitive endpoints, and alert on credentialed requests from origins outside the allowlist.
  • Monitor DNS for dangling records under any domain you trust in CORS, since a takeover there becomes a trusted origin.
  • Track header drift at the edge: a CDN or gateway rule that starts emitting CORS headers on new paths should raise a change alert.

Metrics

  • Percentage of internet-facing applications with a documented, owned CORS allowlist.
  • Number of endpoints returning a reflected or null origin with credentials (target: zero).
  • Count of wildcard-subdomain or HTTP origins in production policies, trending down.
  • Share of repositories with build-breaking rules for permissive CORS settings.
  • Median time to fix CORS findings from scans and penetration tests.

Prevention and hardening

Design rules

  • Do you need CORS at all? Serving the front end and API from the same origin, or routing through a same-origin backend-for-frontend, removes the problem. Add CORS only for real cross-origin consumers.
  • Fixed allowlist: ASVS 5.0 requirement V3.4.2 asks that Access-Control-Allow-Origin is either a fixed value or, if derived from Origin, validated against an allowlist of trusted origins, and that responses using * contain nothing sensitive.
  • Exact comparison: compare the full origin string (scheme, host, port) against configured values. If subdomain patterns are unavoidable, anchor both ends and restrict the pattern to HTTPS.
  • Credentials only where needed: enable Access-Control-Allow-Credentials on the specific routes that require cookies. Token-based APIs that send an Authorization header often need no credentialed CORS.
  • Minimal methods and headers: list methods and headers explicitly rather than using *, and expose response headers with Access-Control-Expose-Headers only when script needs them.
  • Always send Vary: Origin when the allowed origin is chosen per request.
  • Never trust null or HTTP origins in production policies.
  • Keep CSRF defences: ASVS V3.5.1 and V3.5.2 point out that simple requests reach the server without a preflight, so state-changing endpoints still need anti-forgery tokens, a non-safelisted header requirement, or checks on Origin and Content-Type. The OWASP HTML5 Security Cheat Sheet makes the same point.
  • Do not use Origin as authentication: a page cannot set the Origin header (the Fetch Standard lists it as forbidden), but any non-browser client can, so the server must still authenticate and authorise every request.

Choosing a policy model

ModelWhen to choose itTrade-off
No CORS (same origin or backend-for-frontend)First-party web apps and their APIsNeeds routing or proxy work; the strongest option
Static allowlist of exact originsA known, small set of partner or first-party front endsChanges need a deployment; easy to audit
Anchored pattern for one parent domainMany tenant subdomains under a domain you fully controlInherits risk of every subdomain; needs DNS hygiene
Allowlist held in a database or config serviceMulti-tenant platforms where customers register their own originsRegistration becomes a security workflow: validate, approve, expire
* without credentialsPublic, non-sensitive data such as open datasets or public assetsNever for internal, IP-restricted or personalised content

Small organisations with a handful of applications usually do best with no CORS or a static allowlist in code review. Large organisations should centralise policy in a shared library or gateway module with a single owner, so teams do not each write their own matching logic.

Rollout

  1. Inventory: scan every internet-facing host and API for CORS response headers and list the policy, owner and consumers for each.
  2. Triage: fix reflected, null and wildcard-with-credentials cases first, then HTTP and wildcard-subdomain origins.
  3. Observe before enforcing: log the Origin values reaching each endpoint for a few weeks to learn which origins genuinely call it.
  4. Enforce in rings: deploy the tightened allowlist to staging, then a canary slice of production, watching browser console errors and client error rates.
  5. Roll back safely: keep the previous policy as a configuration value so it can be restored quickly, but never roll back to reflection; add a missed origin to the allowlist instead.
  6. Prevent regression: add build-breaking rules, DAST checks and a CORS item in design review for new services.

CORS sits alongside the other browser controls such as Content Security Policy, cookie SameSite attributes and Cross-Origin-Opener-Policy. See the Security Headers Guide.

Common failure patterns

  • Fixing a browser error by reflecting the origin: a developer sees the "wildcard with credentials" error and switches to echoing Origin, creating the worst case.
  • Development settings in production: localhost, null or allow-all values added for local testing ship with the release.
  • Two layers, two policies: the application sends a strict policy while the gateway or CDN adds a permissive one, and the browser sees whichever wins.
  • Trusting everything under the parent domain: a forgotten subdomain with an XSS flaw or a dangling DNS record becomes a trusted origin for the core API.
  • Treating CORS as access control: internal APIs left unauthenticated because "CORS blocks other sites", which does nothing against non-browser clients and fails as soon as a wildcard appears.

Framework mapping

FrameworkReferenceRelevance
OWASP ASVS 5.0V3.4.2, V3.5.1, V3.5.2, V3.5.5Origin allowlisting, CSRF when relying or not relying on preflight, postMessage origin checks
MITRE CWE / CAPECCWE-942; CAPEC-111Permissive cross-domain policy; cross-origin JSON reading
NIST SP 800-53 Rev. 5CM-6, AC-4, SA-11Secure configuration settings, information flow enforcement, developer testing
NIST CSF 2.0PR.PS-01, ID.RA-01Configuration management; identifying and recording vulnerabilities
OWASP WSTGWSTG-CLNT-07Test procedure for CORS

Practitioner checklist

  • Inventory every host and API that emits Access-Control-Allow-Origin and assign an owner.
  • Remove any configuration that reflects the request Origin or allowlists null.
  • Replace substring, prefix and unanchored regex checks with exact origin comparison.
  • Restrict Access-Control-Allow-Credentials: true to routes that need cookies, with explicit origins only.
  • Remove HTTP origins and review every wildcard subdomain against current DNS records.
  • Send Vary: Origin on every dynamically chosen CORS response.
  • Keep CSRF protection on state-changing endpoints regardless of CORS.
  • Make sure only one layer (application or edge) owns CORS headers for each path.
  • Add build-breaking rules for permissive framework settings and authenticated CORS checks to DAST.

Standards and references

Related NHI Mgmt Group resources: Security Headers Guide · Cross-Site Scripting (XSS) Prevention Guide · API Security Guide · Web Application Attacks Guide · CWE Top 25 Guide

PDF for subscribers

Take this guide with you

Every section, table and checklist in one PDF, with links to the sources and related guides. Subscribe to our weekly NHI & AI Identity Journal and we’ll email you a download link.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Written and reviewed by Lalit Choda, NHI Mgmt Group. Last updated 24 October 2026.
NHI Mgmt Group — the #1 independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org

PDF Downloads for Newsletter Subscribers

Get the guide

Enter your details and we’ll email you a download link.

The full guide is free to read online. The PDF is a thank you for subscribing.

Fill in all fields marked * and tick the PDF box to continue.