Command Injection in Express with Api Keys
Command Injection in Express with Api Keys — how this specific combination creates or exposes the vulnerability
Command injection in an Express API that uses API keys often arises when the server validates the key but then passes unchecked external data to a system command. Even with a valid API key confirming caller identity, the endpoint may still be unsafe if it builds shell commands using user-controlled inputs such as filenames, IP addresses, or query parameters. For example, concatenating a user-supplied filename into an exec or spawn call can allow an authenticated caller to inject additional shell commands via shell metacharacters like ;, &&, or backticks.
Consider an Express route that expects an API key in a header and a filename via query string to generate a report. If the implementation does not sanitize or escape the filename, an authenticated caller can supply a value such as report.txt; cat /etc/passwd. When the server builds a shell command, the injected segment runs with the same privileges as the process, potentially exposing sensitive data or enabling further abuse. This pattern violates secure handling of external input and is commonly flagged as an OWASP API Top 10 item related to injection.
Another scenario involves environment variables or configuration values that include user-controlled data. For instance, an API key–scoped integration might invoke a helper that runs ping or curl to validate a remote endpoint. If the target hostname or path is derived from request parameters without strict allowlisting or encoding, an authenticated user can pivot to internal services, trigger SSRF-like behavior, or cause command chaining through crafted input. Even with correct API key checks, missing input validation and unsafe subprocess construction expose the attack surface.
OpenAPI/Swagger specifications can inadvertently document permissive parameters that encourage unsafe usage. If the spec accepts a string for a parameter that is later used in a shell command without type or pattern constraints, runtime behavior may diverge from intended design. middleBrick’s OpenAPI/Swagger spec analysis resolves $ref definitions and cross-references spec definitions with runtime findings, helping to highlight mismatches where an API key–protected endpoint still accepts dangerous input patterns.
Real-world examples include CVE patterns where authenticated endpoints execute system utilities with unsanitized arguments. These findings are surfaced in middleBrick’s security checks, which test unauthenticated attack surfaces and authenticated scenarios when credentials are provided. The scanner does not fix the code but provides prioritized findings with severity levels and remediation guidance, enabling developers to tighten input validation, use allowlists, and avoid shell construction altogether.
Api Keys-Specific Remediation in Express — concrete code fixes
To remediate command injection in Express while preserving API key validation, avoid shell execution entirely. Use native, language-safe operations instead of spawning shell commands for tasks such as file manipulation, network checks, or data conversion. When shell interaction is unavoidable, strict allowlisting, argument arrays, and environment isolation are required.
Insecure example to avoid
const { exec } = require('child_process');
app.get('/report', (req, res) => {
const apiKey = req.get('X-API-Key');
// API key validation omitted for brevity
const filename = req.query.file;
exec(`cat ${filename}`, (err, stdout) => {
if (err) return res.status(500).send('Error');
res.send(stdout);
});
});
In this example, a valid API key does not prevent command injection because filename is directly interpolated into the shell command. An attacker can exploit this even when authenticated.
Safe remediation using native APIs
const fs = require('fs');
app.get('/report', (req, res) => {
const apiKey = req.get('X-API-Key');
// Validate apiKey against a secure store or middleware
const filename = req.query.file;
const allowed = new Set(['report.txt', 'summary.csv', 'metrics.json']);
if (!allowed.has(filename)) {
return res.status(400).send('Invalid file');
}
fs.readFile(`/data/${filename}`, 'utf8', (err, data) => {
if (err) return res.status(500).send('Error');
res.type('txt').send(data);
});
});
This approach removes the shell entirely, uses an allowlist for filenames, and prevents injection regardless of API key validity. The API key can be validated via middleware that checks against a secure key store or a database before allowing access to the handler.
When shell execution is unavoidable: use spawn with argument array
const { spawn } = require('child_process');
app.get('/ping', (req, res) => {
const apiKey = req.get('X-API-Key');
// Validate apiKey
const host = req.query.host;
const allowedHosts = new Set(['api.internal.example', 'monitor.example']);
if (!allowedHosts.has(host)) {
return res.status(400).send('Invalid host');
}
const child = spawn('ping', ['-c', '4', host]);
let output = '';
child.stdout.on('data', (data) => output += data);
child.on('close', (code) => {
if (code !== 0) return res.status(502).send('Unreachable');
res.send(`Ping output: ${output}`);
});
});
Here, arguments are passed as an array, which avoids shell parsing. The host is strictly allowlisted, ensuring that even with a valid API key, only preapproved targets can be probed. This pattern reduces risk while still allowing controlled external command execution.
middleBrick’s scans can validate these practices by checking whether endpoints that use API keys also exhibit unsafe subprocess construction or missing input validation. Developers should pair authentication checks with secure coding patterns to reduce the likelihood of injection across authenticated paths.
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 |