Engineering · Jun 28, 2026 · 6 min read

CORS: reflecting your origin is not the same as leaking your credentials

An Access-Control-Allow-Origin that echoes your probe is a misconfiguration worth noting - one that also returns Access-Control-Allow-Credentials: true is the one that hands a cross-origin attacker your session.

Most CORS findings are raised the instant a scanner sees the server echo back whatever Origin it was sent. That is real, but it is reachability: the trust list is loose. The finding that actually hands a cross-origin attacker your session is the specific pair - a reflected origin returned together with Access-Control-Allow-Credentials: true. Reflection is a note; the pair is a breach.

01Reflection is reachability, the pair is the exploit

The browser will only expose a cross-origin response that carries credentials if two headers agree: the response must name the attacker's exact origin in Access-Control-Allow-Origin and it must also say Access-Control-Allow-Credentials: true. A wildcard origin with credentials is spec-illegal and the browser refuses it. So the dangerous configuration is narrow and specific, which is exactly why it should be confirmed, not assumed.

ACAO reflects any origin, no ACAC misconfiguration, not a credential leak ACAO: * (cannot be sent with ACAC) browser blocks credentialed reads ACAO: <attacker origin> + ACAC: true any web page reads your authenticated data ✗
Only the bottom row is exploitable. The engine raises a critical for that row and a note for the others.

02Confirming the pair without touching a real session

The confirming artifact is a single response: send a request with an attacker-controlled origin and observe the server return both headers for that exact origin. The probe origin is a benign marker domain that holds no real user's cookie, so demonstrating the misconfiguration never exfiltrates a real session or any real data.

cors-pair.httphttp
# request carries a benign, attacker-shaped origin
GET /api/account HTTP/1.1
Origin: https://ap-probe.example
Cookie: <test-account session, never a real user>

# response reflects that exact origin AND allows credentials == confirmed
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://ap-probe.example
Access-Control-Allow-Credentials: true
Proven - CriticalReflected origin with credentials - cross-origin account read - /api/account
Origin: https://ap-probe.example ──► ACAO: https://ap-probe.example + ACAC: true any origin the server is asked to trust is reflected with credentials allowed
Probe origin holds no real session; response body was not read or stored. Critical: any web page can issue credentialed reads against this endpoint.

03Severity follows capability, not presence

The same header family spans three very different findings, and collapsing them into one is how CORS gets both over-reported and under-rated.

ConfigurationWhat it allowsRating
Reflect any origin + ACAC: trueArbitrary cross-origin authenticated readsCritical - confirmed
Reflect any origin, no ACACCross-origin reads of non-credentialed dataMedium - depends on the data
ACAO: * , no ACACPublic data only; browser blocks credentialsLow / informational

04Safe by design, private by default

The controlled origin never carries a real user's cookie, so confirmation cannot leak a real session, and any sensitive values that do appear in a response are masked in the evidence we store. The point is to prove the boundary is open, not to walk through it with someone else's identity.

2
headers that must agree to exploit
0
real sessions used in the probe
1
response is the whole proof

Pin the allowed origins to an explicit allowlist and drop credentials for anything public. The reflected origin stops being a door the moment it stops being dynamic.

Evidence over heuristicsReachable beats foundMonitor → Block: rolling out a gate without slowing teamsIaC-grounded threat modelingAir-gapped AppSec with no phone-homeOne platform vs three tools
See it on your own app →