Api Key Exposure in Strapi with Jwt Tokens
Api Key Exposure in Strapi with Jwt Tokens — how this specific combination creates or exposes the vulnerability
Strapi is a headless CMS that commonly uses JWTs for stateless authentication and session management. When API keys or application secrets are mishandled alongside JWT usage, the combination can expose privileged operations or enable token impersonation. For example, embedding an administrative API key or a signing secret directly in frontend code, client-side JavaScript, or publicly accessible configuration allows an unauthenticated attacker to retrieve the key and forge valid JWTs.
Consider a Strapi backend that issues JWTs after a user logs in but also relies on an environment-scoped API key to call internal services or third-party APIs. If the API key is accidentally exposed—say, through an open CORS policy, a verbose error message, or an unsecured endpoint that returns configuration details—an attacker who obtains the key can generate tokens with elevated scopes by leveraging the known signing secret or by abusing weak token validation.
A concrete attack chain starts when an endpoint leaks configuration or debug data (e.g., a misconfigured GraphQL or REST route). The attacker retrieves the API key or the JWT signing secret, then crafts tokens with modified payloads such as {"role":"admin","scope":"*"}. Because Strapi’s JWT strategy typically validates the token signature but may not enforce strict scope-to-permission mappings on every route, the forged token can grant unintended access to admin-only controllers or data export endpoints. This becomes critical if the token lacks proper expiration checks or if the application trusts client-supplied identifiers without re-verifying permissions on each request.
Insecure defaults compound the issue. For instance, using the default secret key generation in Strapi without rotating the JWT secret, or failing to restrict token issuers and audiences, allows replay across environments. If an API key intended for server-to-server communication is stored in the same configuration namespace as JWT secrets, a single leak can simultaneously compromise both token integrity and backend service authentication. The risk is especially pronounced when tokens are used for authorization without additional context checks, enabling horizontal or vertical privilege escalation across users and collections.
To detect these patterns, middleBrick scans unauthenticated attack surfaces and correlates findings from its 12 security checks—particularly Input Validation, Authentication, Property Authorization, and LLM/AI Security—to surface exposed secrets, overprivileged tokens, and insecure configuration. By mapping results to frameworks like OWASP API Top 10 and PCI-DSS, the scanner highlights specific misconfigurations in how Strapi issues and validates JWTs alongside API key usage.
Jwt Tokens-Specific Remediation in Strapi — concrete code fixes
Remediation centers on strict token validation, separation of secrets, and least-privilege payload design. Always configure Strapi’s JWT strategy with explicit options for expiration, issuer, audience, and algorithms, avoiding permissive defaults.
Secure JWT setup in Strapi
Update your Strapi configuration to enforce strict JWT validation. The following JavaScript example reflects typical ./config/jwt.js settings that limit token scope and enforce server-side signing:
module.exports = {
secret: process.env.JWT_SECRET || 'change-this-to-a-very-long-random-string',
expiresIn: '1h',
algorithms: ['HS256'],
issuer: 'stapi.example.com',
audience: 'stapi-client.example.com',
notBefore: 0,
clockTolerance: 10,
};
Ensure the signing secret is stored in environment variables and never committed to source control. Rotate the secret periodically and avoid sharing it with frontend code or public build artifacts.
Token payload design and scope enforcement
Design token payloads with minimal claims. Instead of broad roles, embed granular scopes and validate them on each request. The following example shows how to customize the payload during user login and validate scopes in a policy:
// In a Strapi controller or custom provider
const token = strapi.plugins['users-permissions'].services.jwt.issue({
id: user.id,
role: user.role,
scopes: ['content.read', 'content.write'],
});
// In an auth policy (e.g., ./api/protected/routes/policies/scope-check.js)
module.exports = (config, { strapi }) => {
return async (ctx, next) => {
const token = ctx.request.header.authorization?.split(' ')[1];
if (!token) return ctx.unauthorized('Missing authorization token');
try {
const payload = strapi.plugins['users-permissions'].services.jwt.verify(token);
if (!payload.scopes || !payload.scopes.includes('content.read')) {
return ctx.forbidden('Insufficient scope');
}
ctx.state.user = { id: payload.id, role: payload.role, scopes: payload.scopes };
await next();
} catch (err) {
return ctx.unauthorized('Invalid token');
}
};
};
Always specify the issuer and audience in token verification to prevent tokens issued by other services from being accepted. In multi-service architectures, validate the aud claim on every endpoint to ensure tokens are used only by intended consumers.
API key segregation and storage
Keep API keys used for backend service communication separate from JWT secrets. Store them in environment variables or a secrets manager, and reference them via process.env in Strapi. For example, when calling an external API from a Strapi service, load the key at runtime:
const externalApiKey = process.env.EXTERNAL_API_KEY;
if (!externalApiKey) {
throw new Error('Missing EXTERNAL_API_KEY');
}
// Use the key in request headers, never log it or expose in responses
await strapi.plugin('strapi-utils').services.http.get('https://api.example.com/data', {
headers: { Authorization: `Bearer ${externalApiKey}` },
});
Combine this with CORS restrictions and input validation policies to ensure leaked keys cannot be exfiltrated through client-facing routes. middleBrick’s checks for Input Validation and BFLA/Privilege Escalation help surface places where API keys might be inadvertently exposed or over-privileged tokens accepted from clients.