跪拜 Guibai
← All articles
Frontend · JavaScript · Programmer

CORS Protects the Browser User, Not the API Server: The Reflected-Origin Hole Is Worse Than `*`

By ErpanOmer ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Credentials-bearing endpoints configured with a reflected Origin are effectively open to any website; an attacker page can replay a bank transfer with the victim's cookies. A two-line CORS fix becomes a CSRF vector unless the team treats allowlisting as a security decision.

Summary

Without CORS, a page on baidu.com could POST to bank.com with the user's bank login cookie and transfer money. That request reaches the server and executes; the browser only blocks the response. CORS is a read-protection layer for the person in front of the browser, not a server-side authentication gate.

`Access-Control-Allow-Origin: *` is fine for public, credential-free endpoints, but browsers refuse to attach cookies when the value is `*`. A supposedly convenient backend pattern reflects the incoming Origin verbatim while setting credentials true, which defeats the browser's safeguard and exposes authenticated APIs to every site. A production CORS setup for Cloudflare Workers uses an origin allowlist, handles OPTIONS preflight, sets Access-Control-Max-Age to 86400, and adds Vary: Origin to keep cached responses safe.

CORS does not stop cross-origin writes. Native form submissions, img, script, and link tags bypass its restrictions, so forms enable classic CSRF and image tags can fire GET URLs with user cookies. GET endpoints must never mutate state, and origin or referer checks belong on every state-changing request.

Takeaways
— CORS constrains browser behavior, not server access; curl, Postman, or backend code can call an API without any CORS header.
— Without the same-origin policy, a malicious page could POST to bank.com with the user's stored cookies and complete a transfer.
— Access-Control-Allow-Origin: * is safe for public, credential-free endpoints, but browsers refuse to combine `*` with credentials: include.
— Reflecting the request Origin in Access-Control-Allow-Origin while setting credentials true allows credentialed cross-origin requests from any website, making it worse than `*`.
— Simple requests (GET, HEAD, or POST with no custom headers and form-urlencoded, multipart/form-data, or text/plain content types) are sent before the browser checks whether JavaScript may read the response.
— Non-simple requests trigger an OPTIONS preflight; Access-Control-Max-Age: 86400 lets the browser cache that preflight for 24 hours, otherwise every non-simple request doubles the request count.
— Native form submissions, img, script, and link tags bypass CORS restrictions; a form POST can carry user cookies and enable CSRF.
— CORS defends against cross-origin reading, not writing; CSRF protection requires origin or referer validation on state-changing requests.
— GET endpoints that mutate state can be triggered remotely by image tags and must be redesigned.
Conclusions

Asking the backend to add CORS is usually treated as routine config, but for authenticated APIs it is a security boundary decision; the right follow-up is whether the endpoint requires a login session.

Reflecting Origin can be more dangerous than `*` because it sidesteps the browser's built-in wildcard-with-credentials restriction, turning CORS into the exact vulnerability it exists to prevent.

Since CORS only blocks response reading, a backend's trust boundary is incomplete: any GET endpoint with side effects is remotely triggerable by an image tag.

The common split between simple and preflighted requests means a denied response can still be fully processed by the server, so CORS gives no assurance that a request wasn't executed.

Concepts & terms
Same-origin policy
A browser rule that prevents JavaScript from one origin (scheme, host, port) from reading responses sent to another origin; forms and subresource tags are treated differently for triggering requests.
CORS preflight
An OPTIONS request browsers send before a non-simple cross-origin request, asking the server which methods and headers are allowed; the result can be cached with Access-Control-Max-Age.
CSRF
Cross-site request forgery, where a malicious page makes the browser send a state-changing request with the victim's cookies; CORS does not block this because it only restricts response reading.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗