Command Injection with Api Keys
How Command Injection Manifests in Api Keys
Command injection in API keys typically occurs when untrusted input from API keys is used to construct shell commands or execute system-level operations. This vulnerability is particularly dangerous because API keys often grant programmatic access to external services, creating opportunities for attackers to execute arbitrary commands on the server or the external service itself.
The most common pattern involves API keys being concatenated into command strings without proper sanitization. For example, when an API key is passed to a subprocess call or shell command, an attacker can craft a key containing shell metacharacters that break out of the intended command context.
// Vulnerable pattern in Node.js using child_process
const { exec } = require('child_process');
const apiKey = req.query.apiKey;
exec(`curl -X POST https://api.example.com/data -H "Authorization: Bearer ${apiKey}"`, (err, stdout, stderr) => {
console.log(stdout);
});
In this example, if an attacker provides an API key like validkey"; rm -rf /; echo ", the command becomes:
curl -X POST https://api.example.com/data -H "Authorization: Bearer validkey"; rm -rf /; echo "This executes the intended curl command, then runs rm -rf /, potentially deleting critical files. The double quotes are terminated early, allowing arbitrary command injection.
Another manifestation occurs in API key validation systems that use external command-line tools. Consider a system that validates JWT tokens using OpenSSL:
// Vulnerable JWT validation using system commands
const apiKey = req.headers.authorization.replace('Bearer ', '');
const verifyCommand = `echo '${apiKey}' | openssl base64 -d | openssl dgst -sha256 -verify public.pem -signature signature.bin`;
exec(verifyCommand, (err, stdout, stderr) => {
if (stdout.includes('Verified OK')) {
// Proceed with authenticated request
}
});
An attacker could craft an API key containing shell metacharacters to execute arbitrary commands on the validation server.
API keys used in database queries that execute system commands represent another attack vector. Some applications use API keys to dynamically construct database commands that include system-level operations:
// Vulnerable pattern in Python with subprocess
import subprocess
def process_api_key(api_key):
# Malicious API key: valid_key; cat /etc/passwd; echo "
command = f'process_data.sh {api_key}'
subprocess.run(command, shell=True) # shell=True is dangerous!
The shell=True parameter is particularly dangerous as it enables shell metacharacter interpretation, allowing attackers to chain multiple commands separated by semicolons, ampersands, or pipes.
Api Keys-Specific Detection
Detecting command injection vulnerabilities in API key handling requires both static code analysis and dynamic testing. For static analysis, look for patterns where API keys are passed to functions that execute system commands without proper sanitization.
Key detection patterns include:
- API keys concatenated into command strings with shell metacharacters
- Use of
shell=True(Python) or equivalent unsafe flags - API keys passed to
exec(),eval(),system(), or similar functions - Dynamic command construction using API key values
- API keys used in
eval()or similar dynamic execution contexts
Dynamic testing involves attempting to inject shell metacharacters through API keys and observing the system's response. Common injection payloads include:
validkey; echo 'injection_test';
validkey || echo 'injection_test';
validkey && echo 'injection_test';
validkey | echo 'injection_test';
validkey$(echo 'injection_test')
When testing, monitor for unexpected command execution, error messages that reveal system information, or changes in application behavior.
middleBrick's API security scanner specifically tests for command injection vulnerabilities in API endpoints. The scanner automatically attempts to inject shell metacharacters through API keys and analyzes the responses for signs of successful injection. It tests all 12 security categories including input validation and unsafe consumption patterns.
For comprehensive testing, middleBrick's black-box scanning approach sends crafted API key payloads to your endpoints and analyzes the responses for indicators of command injection, such as:
- Unexpected output containing injected commands
- Changes in response timing or structure
- Error messages revealing system information
- Unexpected side effects on the system
The scanner provides a security score (A-F) and detailed findings with severity levels, helping you prioritize remediation efforts.
Api Keys-Specific Remediation
Remediating command injection vulnerabilities in API key handling requires eliminating unsafe patterns and implementing proper input validation. The primary approach is to avoid using shell commands altogether when possible, and when unavoidable, use safe alternatives.
For Node.js applications, replace unsafe exec() calls with the spawn() or execFile() functions, which don't invoke a shell by default:
// Safe approach using execFile (no shell interpretation)
const { execFile } = require('child_process');
const apiKey = req.query.apiKey;
// Validate API key format before use
if (!/^[a-zA-Z0-9_-]{20,64}$/.test(apiKey)) {
return res.status(400).json({ error: 'Invalid API key format' });
}
execFile('curl', ['-X', 'POST', 'https://api.example.com/data', '-H', `Authorization: Bearer ${apiKey}`], (err, stdout, stderr) => {
console.log(stdout);
});
The key improvements here are using execFile() instead of exec() and adding strict API key validation with a regular expression that only allows expected characters.
For Python applications, use the subprocess.run() function with shell=False and pass arguments as a list:
import subprocess
import re
def process_api_key(api_key):
# Strict validation - only alphanumeric, hyphens, and underscores
if not re.match(r'^[a-zA-Z0-9_-]{20,64}$', api_key):
raise ValueError('Invalid API key format')
# Safe approach - no shell=True, arguments as list
command = ['process_data.sh', api_key]
result = subprocess.run(command, capture_output=True, text=True)
return result.stdout
Another effective remediation is to use API client libraries instead of constructing shell commands. For HTTP requests, use dedicated HTTP clients:
// Using axios instead of shell commands
const axios = require('axios');
const apiKey = req.query.apiKey;
// Validate API key format
if (!/^[a-zA-Z0-9_-]{20,64}$/.test(apiKey)) {
return res.status(400).json({ error: 'Invalid API key format' });
}
try {
const response = await axios.post('https://api.example.com/data', {
headers: {
'Authorization': `Bearer ${apiKey}`
}
});
console.log(response.data);
} catch (error) {
console.error(error);
}
This approach completely eliminates the command injection surface by using a proper HTTP client library instead of shell commands.
For applications that must use system commands, implement a strict allowlist of allowed characters and commands. Never allow shell metacharacters like ;, &&, ||, |, $(), or backticks in API keys.
middleBrick's GitHub Action can be integrated into your CI/CD pipeline to automatically scan for command injection vulnerabilities before deployment. The action can fail builds if security scores drop below your threshold, ensuring these issues are caught early.
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 |