Command Injection in Fastapi with Dynamodb
Command Injection in Fastapi with Dynamodb — how this specific combination creates or exposes the vulnerability
Command Injection occurs when untrusted input is passed to a system command without proper validation or escaping. In a Fastapi application that interacts with DynamoDB, the risk emerges not from DynamoDB itself—DynamoDB is a NoSQL database and does not execute shell commands—but from how application code constructs and invokes operating system commands using data that may originate from or be influenced by DynamoDB records.
For example, a Fastapi endpoint might query DynamoDB for a file name or a system identifier, then pass that value to a shell utility such as subprocess.run or os.system without sanitization. If an attacker can control the data stored in or retrieved from DynamoDB—perhaps by writing a malicious value through another API path or exploiting insufficient access controls—they can inject shell metacharacters like ;, &, or | that alter command behavior. This chain—DynamoDB as a data source, Fastapi as the orchestrator, and a shell command as the execution target—creates a viable injection path.
Consider a scenario where an endpoint retrieves an object key from DynamoDB and uses it to generate a report via a command-line tool. A vulnerable implementation might look like this:
import subprocess
from fastapi import FastAPI
import boto3
app = FastAPI()
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Items')
@app.get('/generate/{item_id}')
def generate_report(item_id: str):
response = table.get_item(Key={'id': item_id})
item = response.get('Item', {})
filename = item.get('filename', 'report')
# Unsafe: user-influenced data from DynamoDB used in shell command
result = subprocess.run(['generate-report', '--name', filename], capture_output=True, text=True)
return {'output': result.stdout}
If the filename attribute in DynamoDB contains something like report; rm -rf /, the command executed becomes generate-report --name report; rm -rf /, causing unintended system operations. Even if DynamoDB access is restricted, other vectors—such as log injection or secondary data stores—might feed attacker-controlled values into command construction.
The LLM/AI Security checks included in middleBrick specifically test for scenarios where system prompts or outputs could indirectly influence command construction, and they help identify risky patterns such as unchecked concatenation of external data into shell invocations. While middleBrick does not fix these issues, its findings provide prioritized guidance and remediation steps to help developers address such risks.
Dynamodb-Specific Remediation in Fastapi — concrete code fixes
Defending against Command Injection when Fastapi interacts with DynamoDB requires strict input validation, avoiding shell metacharacters in dynamic arguments, and using safe execution patterns. Below are concrete, executable code examples that demonstrate secure practices.
1. Avoid Shell=True and Use Argument Lists
Never use shell=True, and always pass commands as a list of arguments. This prevents the shell from interpreting metacharacters. Validate and sanitize any string that originates from DynamoDB before use.
import subprocess
from fastapi import FastAPI, HTTPException
import re
import boto3
app = FastAPI()
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Items')
def is_safe_filename(value: str) -> bool:
# Allow only alphanumeric, underscore, hyphen, and limited safe extensions
return re.match(r'^[A-Za-z0-9_.\-]+$', value) is not None
@app.get('/generate/{item_id}')
def generate_report(item_id: str):
response = table.get_item(Key={'id': item_id})
item = response.get('Item', {})
filename = item.get('filename', 'report')
if not is_safe_filename(filename):
raise HTTPException(status_code=400, detail='Invalid filename')
# Safe: argument list, no shell interpretation
result = subprocess.run(['generate-report', '--name', filename], capture_output=True, text=True)
return {'output': result.stdout}
2. Use AWS SDK Operations Instead of Shell Commands
Whenever possible, perform operations directly via the AWS SDK rather than invoking external commands. This eliminates shell injection risk entirely and is more efficient.
from fastapi import FastAPI, HTTPException
import boto3
from botocore.exceptions import ClientError
app = FastAPI()
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Items')
@app.get('/items/{item_id}')
def get_item_details(item_id: str):
try:
response = table.get_item(Key={'id': item_id})
item = response.get('Item')
if not item:
raise HTTPException(status_code=404, detail='Item not found')
# Direct SDK usage avoids shell entirely
return item
except ClientError as e:
raise HTTPException(status_code=500, detail=str(e))
3. Input Validation and Allowlisting
Apply strict allowlists for any data used in command-like contexts. For identifiers, prefer UUIDs or numeric IDs that match a predictable pattern, and reject any input containing shell metacharacters.
import re
def validate_identifier(value: str) -> bool:
# Strict alphanumeric and hyphen pattern
return re.fullmatch(r'[A-Za-z0-9\-]+', value) is not None
Use this validator before constructing any external call. Combine these practices with middleBrick’s scanning workflow—using the CLI to run middlebrick scan <url> or integrating the GitHub Action to fail builds on risky findings—to continuously surface such issues during development.
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 |
Frequently Asked Questions
Does DynamoDB itself introduce command injection risks?
How can I test my Fastapi endpoints for command injection during development?
middlebrick scan <your-api-url>. The scan performs black-box testing, including checks for injection-like behaviors, and provides prioritized findings with remediation guidance without requiring authentication or configuration.