Api Key Exposure in Strapi (Typescript)
Api Key Exposure in Strapi with Typescript — how this specific combination creates or exposes the vulnerability
Strapi is a headless CMS that often stores sensitive credentials such as database passwords and third-party service tokens within its codebase or environment configuration. When developers write business logic in Typescript controllers or services, they may inadvertently expose API keys through insecure logging, client-side serialization, or overly permissive GraphQL/REST field resolvers.
One common pattern is fetching a third-party service key from environment variables and passing it directly to a frontend-serializable object or returning it in an API response. Because Strapi applications built with Typescript can include custom controllers and services, a developer might write code like the following, accidentally returning a key to an unauthenticated caller:
// src/api/integration/controllers/integration.controller.ts
import { createCoreController } from '@strapi/strapi';
export default createCoreController('api::integration.integration', ({ strapi }) => ({
async findOne(ctx) {
const entry = await strapi.entityService.findOne('api::integration.integration', ctx.params.id);
// Vulnerable: exposing an API key to the client
return { ...entry, apiKey: process.env.THIRD_PARTY_API_KEY };
},
}));
Additionally, Strapi’s Typescript services may be used to call external APIs, and if error handling in Typescript does not sanitize key material from stack traces or logs, keys can be leaked through application logs or error responses. For example, a poorly handled request may log the full environment configuration including keys:
// src/api/webhook/controllers/webhook.controller.ts
export async function handleWebhook(ctx) {
const payload = ctx.request.body;
strapi.log.info(`Processing webhook with config: ${JSON.stringify(process.env)}`);
// ... rest of logic
}
Another exposure vector is through OpenAPI spec generation when Strapi plugins introspect environment variables for runtime documentation, and the Typescript layer exposes these values through an introspection endpoint. Even when using Strapi’s built-in security controls, a developer using Typescript must ensure that any middleware or policy does not forward keys to the client or include them in serialized JSON that traverses the network.
Because middleBrick scans the unauthenticated attack surface and performs both OpenAPI/Swagger spec analysis and runtime checks, it can detect endpoints where an API key appears in responses or documentation. The scanner’s checks for Data Exposure and Unsafe Consumption are particularly effective at highlighting places where secrets leak through API surfaces built with Typescript in Strapi.
Typescript-Specific Remediation in Strapi
Remediation focuses on ensuring that API keys never leave the server environment and that Typescript code enforces strict data filtering. Instead of returning raw environment variables, use server-side-only methods to access secrets and transform responses before serialization.
First, use Strapi’s built-in configuration and runtime environment access without exposing keys. Create a service that handles external calls without leaking credentials:
// src/api/integration/services/integration.ts
export const callExternalService = async (recordId: number) => {
const { apiKey } = strapi.config.get('plugin.thirdParty');
const response = await fetch('https://api.example.com/data', {
headers: { Authorization: `Bearer ${apiKey}` },
});
if (!response.ok) {
throw new Error('External service unavailable');
}
return response.json();
};
Then, in your controller, only return safe, filtered data to the client:
// src/api/integration/controllers/integration.controller.ts
import { createCoreController } from '@strapi/strapi';
import { callExternalService } from '../services/integration';
export default createCoreController('api::integration.integration', ({ strapi }) => ({
async findOne(ctx) {
const entry = await strapi.entityService.findOne('api::integration.integration', ctx.params.id);
// Safe: only non-sensitive fields are returned
const { apiKey, ...safeEntry } = entry;
return safeEntry;
},
}));
For logging, avoid serializing environment objects and scrub sensitive fields explicitly:
// src/api/webhook/controllers/webhook.controller.ts
export async function handleWebhook(ctx) {
const payload = ctx.request.body;
// Safe: log only necessary metadata
strapi.log.info(`Processing webhook for entity ${payload.entityId}`);
// ... rest of logic
}
When using middleBrick’s CLI or Web Dashboard, you can validate that these fixes work by rescanning the endpoint. The CLI allows you to automate checks within a TypeScript project:
npx middlebrick scan https://your-strapi-api.com/api/integration --output json
Pro plan users can enable continuous monitoring to ensure that any future changes to Typescript controllers do not reintroduce key exposure. The GitHub Action can be configured to fail builds if a scan detects exposed secrets, providing a CI/CD gate before deployment.