Cors Wildcard with Bearer Tokens
How CORS Wildcard Manifests in Bearer Tokens
When an API uses Bearer tokens for authentication, the token is typically transmitted in the Authorization header. If the server’s CORS policy is misconfigured with a wildcard origin (Access-Control-Allow-Origin: *) and also lists the Authorization header in Access-Control-Allow-Headers, a malicious web site can cause a victim’s browser to send a cross‑origin request that includes the bearer token and read the response.
The attack flow is:
- Victim logs into the legitimate API and receives a Bearer token (e.g.,
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…). - Victim visits an attacker‑controlled site that runs JavaScript.
- The attacker’s script creates an
XMLHttpRequestorfetchcall tohttps://api.example.com/profilewith theAuthorizationheader set toBearer <token>. - Because the request includes a custom header, the browser issues a preflight
OPTIONSrequest. - The API responds with
Access-Control-Allow-Origin: *andAccess-Control-Allow-Headers: Authorization. Since the origin is a wildcard and no credentials are required (the token is not a cookie), the browser allows the actual request. - The request reaches the API, the bearer token is validated, and the API returns sensitive data (e.g., user profile). The attacker’s script can now read that data.
This pattern appears in any code path where the API validates a Bearer token before returning data, such as:
GET /user/me– returns the authenticated user’s record.POST /orders– creates an order after checking the token.GET /api/v1/resources/:id– returns a resource if the token grants access.
The vulnerability is not specific to any language; it shows up in Express.js, Django REST Framework, Spring Boot, or any framework that lets developers configure CORS globally.
Bearer Tokens‑Specific Detection
Detecting this issue requires checking two related CORS response headers together with the presence of an Authorization header in the allowed list. middleBrick performs this check automatically during its 12‑parallel security scans.
When you submit a URL (e.g., https://api.example.com) via the dashboard, CLI, or GitHub Action, middleBrick:
- Sends an
OPTIONSrequest to each discovered endpoint. - Examines the
Access-Control-Allow-OriginandAccess-Control-Allow-Headersheaders. - Flags a finding if:
Access-Control-Allow-Originis exactly*(wildcard).Access-Control-Allow-HeaderscontainsAuthorization(case‑insensitive) or is set to*.- Assigns a severity based on the endpoint’s sensitivity (e.g., endpoints that return user data or perform state‑changing actions receive a higher severity).
Example CLI output (truncated for clarity):
$ middlebrick scan https://api.example.com
Scanning... (12 checks in parallel)
[+] CORS Misconfiguration – High
Endpoint: GET /user/me
Issue: Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Authorization, Content-Type
Impact: Attacker site can read bearer‑token‑protected data.
Remediation: Restrict origin to trusted domains or remove Authorization from allowed headers.
Security Score: C (68/100)
The same detection appears in the web dashboard under the “Findings” tab, and in the GitHub Action as a check that can fail a build if the score drops below a threshold you define (e.g., fail on any “High” or higher finding). The MCP Server integration lets you trigger a scan directly from your IDE, showing the CORS warning alongside other issues before you commit code.
Bearer Tokens‑Specific Remediation
The fix is to tighten the CORS policy so that either the origin is not a wildcard when the Authorization header is allowed, or the Authorization header is removed from the allowed list for wildcard origins. Below are language‑specific examples using the native libraries most teams already rely on.
Node.js / Express with the cors package
Incorrect (wildcard + Authorization):
const cors = require('cors');
app.use(cors({
origin: '*',
methods: ['GET','POST'],
allowedHeaders: ['Authorization','Content-Type']
}));
Correct – restrict origin to known trusted origins:
const cors = require('cors');
const allowedOrigins = ['https://app.example.com', 'https://portal.example.com'];
app.use(cors({
origin: function (origin, callback) {
// allow requests with no origin (like mobile apps or curl)
if (!origin) return callback(null, true);
if (allowedOrigins.indexOf(origin) !== -1) {
return callback(null, true);
}
const msg = 'The CORS policy for this site does not allow access from the specified Origin.';
return callback(new Error(msg), false);
},
methods: ['GET','POST'],
allowedHeaders: ['Content-Type'] // Authorization removed; token sent via cookie or same‑site header
}));
If you must keep the Authorization header (e.g., for SPA that cannot use cookies), lock down the origin:
app.use(cors({
origin: allowedOrigins,
methods: ['GET','POST'],
allowedHeaders: ['Authorization','Content-Type']
}));
Python / Django REST Framework
Incorrect (global wildcard):
# settings.py
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_HEADERS = (
'authorization',
'content-type',
)
Correct – whitelist origins and keep Authorization only when needed:
# settings.py
CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = (
'https://app.example.com',
'https://portal.example.com',
)
CORS_ALLOW_HEADERS = (
'authorization', # keep if your frontend truly needs to send it
'content-type',
))
If you prefer not to send the token via a custom header, switch to an HttpOnly, SameSite=Strict cookie and remove authorization from CORS_ALLOW_HEADERS.
Java / Spring Boot
Incorrect (global registry):
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("Authorization","Content-Type");
}
};
}
}
Correct – restrict origins:
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("https://app.example.com","https://portal.example.com")
.allowedHeaders("Authorization","Content-Type");
}
};
}
}
Alternatively, if you want to keep the wildcard for public resources, exclude the Authorization header from allowedHeaders for those mappings.
After applying any of these fixes, re‑run middleBrick (via CLI, dashboard, GitHub Action, or MCP Server) to confirm the CORS finding disappears and the 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 |
Frequently Asked Questions
Does a wildcard CORS policy always lead to a bearer‑token theft?
Authorization header (or *) in Access-Control-Allow-Headers. If the header is not allowed, the browser will block the request before it can include the token, so the token cannot be read by an attacker.Can I still use bearer tokens with a strict CORS policy?
Authorization header from the allowed list or keeps it only when the origin is whitelisted. Many SPAs store the token in memory and attach it to requests; as long as the server trusts the origin, the token is sent and validated normally.