If you've built anything with a frontend that talks to a backend API, you've met CORS. And if you're like most developers, you met it as an error in the browser console that blocked your own legitimate request, and you fixed it by making the policy as permissive as possible until the error went away. That 'fix' may have opened your API to any website on the internet, and you'd never know unless someone tested for it.
▸ https://github.com/tarunjaswani
Cross-Origin Resource Sharing is the browser mechanism that controls which websites can make requests to your API. When it's configured correctly, it's a powerful boundary. When it's misconfigured — which it almost always is, because the error messages push developers toward permissiveness rather than precision — it becomes one of the most common, most exploitable web security weaknesses. This article shows you both sides: the developer's mistake and the attacker's opportunity.
▸ https://github.com/tarunjaswani/CORS-Misconfiguration/tree/main
What CORS Actually Does (in One Paragraph)
When a browser on site A makes a request to an API on site B, the browser asks site B's server: 'Are you okay with site A reading your response?' The server answers via HTTP headers — specifically, Access-Control-Allow-Origin. If the server says yes to site A, the browser releases the response. If it says no (or says nothing), the browser blocks the response from reaching the JavaScript. This is the entire mechanism. CORS is the server telling the browser which external sites are allowed to read its responses.
How Developers Break It
MISTAKE 1 — ALLOW-ORIGIN: * ON AUTHENTICATED ENDPOINTS
The wildcard '*' means 'any website on the internet can read this response.' For truly public data — a public API with no authentication, serving non-sensitive data — this is fine. For anything behind authentication, it's a disaster. If your authenticated API returns Access-Control-Allow-Origin: *, any malicious website a user visits can make authenticated requests to your API (using the user's cookies) and read the response. The browser sends the cookies; the wildcard lets the malicious site read what comes back.
MISTAKE 2 — REFLECTING THE ORIGIN HEADER WITHOUT CHECKING IT
A common pattern: the server reads the Origin header from the incoming request and echoes it back in Access-Control-Allow-Origin, so every requesting site gets 'allowed.' Developers do this because it 'works' for every frontend they test. An attacker sees it too: their site sends a request with their origin, the server echoes it, and the browser says 'allowed.' It's a wildcard with extra steps.
▸ https://x.com/TJaswani7857
MISTAKE 3 — TRUSTING SUBSTRINGS OR REGEX POORLY
Some configurations check whether the origin contains or starts with a trusted domain. An attacker registers a domain like 'trusted-site.com.evil.com' or 'evil-trusted-site.com' and the check passes. Substring matching on security boundaries is a recurring source of bypasses in CORS, CSP, and URL validation alike.
What an Attacker Does With a Broken CORS
- Finds the misconfiguration. Send a request with a test Origin header and check whether the response reflects it or returns a wildcard with credentials allowed. This takes seconds and is trivially automated.
- Builds an exploit page. A simple HTML page with a few lines of JavaScript that makes a credentialed request to your API. No special tools needed — just a browser and a web page.
- Hosts the page and lures a victim. If the target user visits the attacker's page while logged into your app, the browser sends their cookies to your API, the API responds, and the attacker's JavaScript reads the response. User data, account details, whatever the API returns — exfiltrated silently. How to Fix It • Never use Access-Control-Allow-Origin: * on any endpoint that uses authentication or returns sensitive data. Whitelist the specific origins that need access and return only those. • Don't reflect the Origin header blindly. Maintain an explicit allowlist of trusted origins and check against it exactly — not with contains, startsWith, or regex that can be tricked. • Set Access-Control-Allow-Credentials: true only when you've verified the origin against your allowlist. Credentials plus a weak origin check is the exploitable combination. • Use a CORS configuration library your framework provides rather than hand-rolling headers — it's harder to make the allowlist mistakes with a well-tested library than with manual header setting. • Test your own configuration. Send requests with unexpected Origin headers and see what your server does. The tool linked below automates this check.
CORS errors in development push you toward the permissive fix. The permissive fix, shipped to production, opens your API to any website on the internet. The gap between 'it works in development' and 'it's secure in production' is exactly one misconfigured header — and most teams never check it after the console error disappears. Check yours.
— Tarun Jaswani
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.