Cors Wildcard with Jwt Tokens
How CORS Wildcard Manifests in JWT Tokens
When an API relies on JWT tokens carried in the Authorization header, a misconfigured CORS policy can unintentionally expose those tokens to any web origin. The classic dangerous pattern is:
Access-Control-Allow-Origin: *Access-Control-Allow-Credentials: true(or the header is omitted, causing the browser to treat the request as credential‑less while still reflecting the Origin)
If the API reflects the request’s Origin value back in Access-Control-Allow-Origin when credentials are involved, an attacker can lure a victim’s browser to make a cross‑origin request that includes the victim’s JWT (either as a cookie‑based token or via localStorage sent as a custom header). Because the response is now readable by the attacker’s origin, the JWT can be harvested and replayed.
This issue appears in the request‑handling path before authentication logic runs. For example, an Express endpoint that checks req.headers.authorization for a Bearer JWT will still process the request even if the CORS middleware has already allowed the attacker’s origin and leaked the token in the response.
Real‑world impact: CVE‑2020-15257 (Spring Framework) demonstrated how a reflected CORS header combined with token‑based authentication led to token theft and account takeover. The OWASP API Security Top 10 2023 lists this under API2:2023 Broken Authentication, as the token can be stolen and used to impersonate the user.
JWT Tokens-Specific Detection
middleBrick detects this misconfiguration by performing a black‑box CORS probe as part of its 12 parallel checks. The scanner:
- Sends an
OPTIONSrequest (or a simple GET with custom headers) containing an attacker‑controlledOriginvalue (e.g.,https://evil.example.com). - Examines the response for:
Access-Control-Allow-Originthat either mirrors the supplied origin or is set to*.Access-Control-Allow-Credentials: truepresent when the origin is not a fixed trusted list.- The presence of an
Authorizationheader in the request (indicating token‑based auth) and, crucially, whether the response body contains data that would be useful to an attacker (often the API returns user profile or token details). - If the origin is reflected and credentials are allowed, middleBrick flags the finding as CORS wildcard with credential‑enabled endpoint and tags it with the Broken Authentication category, providing a severity of high.
The scanner does not need any credentials or agents; it works solely against the unauthenticated attack surface. For JWT‑specific context, middleBrick also checks whether the endpoint expects a JWT (by looking for the Bearer pattern in the Authorization header of normal requests) and notes that the exposed token could be harvested.
Example of a middleBrick CLI invocation that would reveal this issue:
middlebrick scan https://api.example.com/users/me
The resulting report would include a finding similar to:
- Finding: CORS header
Access-Control-Allow-Origin: *combined withAccess-Control-Allow-Credentials: true - Endpoint: GET /users/me
- Impact: Potential JWT token leakage via cross‑origin request
- Remediation: Restrict allowed origins and disable credentials for wildcard origins.
JWT Tokens-Specific Remediation
Fixing the issue requires aligning the CORS policy with the authentication mechanism. The following principles apply:
- Never use
Access-Control-Allow-Origin: *when the request includes credentials (cookies, HTTP auth, or client‑sent JWT headers). - Maintain an explicit list of trusted origins.
- If credentials are not needed, set
Access-Control-Allow-Credentials: false(or omit the header) and keep the wildcard only for public resources.
Below are concrete, syntactically correct examples for common stacks.
Node.js / Express with the cors package
Misconfigured:
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: '*', // ❌ wildcard
credentials: true // ❌ enables credentials with wildcard
}));
app.get('/api/profile', (req, res) => {
const token = req.headers.authorization; // Expects Bearer JWT
// … token validation …
res.json({ user: getUserFromToken(token) });
});
app.listen(3000);
Corrected:
const express = require('express');
const cors = require('cors');
const app = express();
const allowedOrigins = [
'https://app.example.com',
'https://portal.example.com'
];
app.use(cors({
origin: allowedOrigins, // ✅ explicit list
credentials: true // ✅ safe because origin is not a wildcard
}));
app.get('/api/profile', (req, res) => {
const token = req.headers.authorization;
// Validate JWT
res.json({ user: getUserFromToken(token) });
});
app.listen(3000);
Python / Flask with flask-cors
Misconfigured:
from flask import Flask, request, jsonify
from flask_cors import CORS
app = Flask(__name__)
CORS(app, origins='*', supports_credentials=True) # ❌
@app.route('/me')
def me():
auth = request.headers.get('Authorization')
# Expects Bearer JWT
return jsonify(user=decode_jwt(auth))
if __name__ == '__main__':
app.run()
Corrected:
from flask import Flask, request, jsonify
from flask_cors import CORS
app = Flask(__name__)
trusted_origins = [
'https://app.example.com',
'https://portal.example.com'
]
CORS(app, origins=trusted_origins, supports_credentials=True) # ✅
@app.route('/me')
def me():
auth = request.headers.get('Authorization')
return jsonify(user=decode_jwt(auth))
if __name__ == '__main__':
app.run()
After applying the fix, rerun middleBrick (via CLI, Dashboard, GitHub Action, or MCP Server) to verify that the CORS wildcard finding disappears and the API’s security score improves.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |