Injection Flaws in Fastapi with Dynamodb
Injection Flaws in Fastapi with Dynamodb
Injection flaws occur when untrusted data is interpreted as part of a command or query. In a Fastapi service that uses Dynamodb, the risk centers on how input is translated into DynamoDB expressions and how pagination or filtering is built. If user-controlled values are concatenated into an expression attribute names map, or if string-based key conditions are built without strict validation, attackers can attempt to inject expressions that change the semantics of the request.
Fastapi provides automatic request validation via Pydantic, which reduces some injection risks by enforcing types and constraints. However, developers can still introduce injection-like behavior by dynamically constructing ExpressionAttributeNames or ExpressionAttributeValues from unchecked inputs. For example, using a user-supplied field name directly in a KeyConditionExpression can lead to unintended attribute selection or bypass of intended partition key constraints.
Consider a search endpoint that accepts a filter string and builds a Query request. If the filter key is derived from user input without strict allowlisting, an attacker might supply a value like #status = :status in a field intended to be a literal attribute name. If ExpressionAttributeNames is constructed naively, this could map to a sensitive attribute such as aws:dynamodb:LeadingKeys or enable scanning across unintended attributes. While DynamoDB’s parameterization with placeholders helps, the construction of the expression on the server side remains the source of risk.
Another scenario involves pagination or filtering on non-key attributes. If sort keys or filter expressions are built using string interpolation instead of structured parameter binding, injection-like behavior can occur. For instance, a filter expression such as price >= :min and price <= :max is safe when using ExpressionAttributeValues, but if the attribute name itself is dynamic (e.g., user_input + '_status') and mapped via ExpressionAttributeNames without strict validation, the effective access pattern can be altered.
The combination of Fastapi’s automatic request parsing and Dynamodb’s expression syntax requires careful handling. Always prefer parameterized expressions, validate field names against an allowlist, and avoid assembling key condition expressions from raw strings. Treat any input that influences expression structure as potentially malicious, even when using an ORM or higher-level wrapper.
Dynamodb-Specific Remediation in Fastapi
Remediation focuses on strict input validation, safe expression construction, and using DynamoDB’s built-in parameterization. Below are concrete, safe patterns for Fastapi endpoints that query DynamoDB.
1. Use allowlisted field names for ExpressionAttributeNames. Never directly interpolate user input into attribute names.
from fastapi import FastAPI, Query, HTTPException
import boto3
from pydantic import BaseModel
app = FastAPI()
dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
table = dynamodb.Table('Items')
# Safe: allowlist mapping from client-friendly names to actual attribute names
ALLOWED_SORT_KEYS = {'created_at', 'name', 'updated_at'}
ALLOWED_FILTER_ATTRS = {'status', 'category'}
class Item(BaseModel):
pk: str
sk: str
status: str
name: str
@app.get('/items/')
def list_items(
pk: str = Query(..., description='Partition key'),
sort_key: str = Query('created_at', description='Sort key field'),
filter_status: str | None = Query(None, description='Filter by status')
):
if sort_key not in ALLOWED_SORT_KEYS:
raise HTTPException(status_code=400, detail='Invalid sort key')
expression_attribute_names = {f'#sk': sort_key}
key_condition = 'pk = :pk and #sk >= :start'
expression_attribute_values = {
':pk': pk,
':start': '2024-01-01T00:00:00Z'
}
response = table.query(
KeyConditionExpression=key_condition,
ExpressionAttributeNames=expression_attribute_names,
ExpressionAttributeValues=expression_attribute_values,
Limit=10
)
return response.get('Items', [])
2. For filter expressions on allowed attributes, map user input to safe attribute names and use placeholders for values.
filter_attr = 'status'
if filter_attr not in ALLOWED_FILTER_ATTRS:
raise HTTPException(status_code=400, detail='Invalid filter attribute')
expression_attribute_names = {
'#sk': sort_key,
'#st': filter_attr
}
key_condition = 'pk = :pk and #sk >= :start and #st = :status_val'
expression_attribute_values = {
':pk': pk,
':start': '2024-01-01T00:00:00Z',
':status_val': filter_status or 'active'
}
response = table.query(
KeyConditionExpression=key_condition,
ExpressionAttributeNames=expression_attribute_names,
ExpressionAttributeValues=expression_attribute_values,
Limit=10
)
3. Avoid constructing KeyConditionExpression from concatenated strings. Always keep the partition key literal and validate the sort key component.
# Unsafe: building expression from raw strings
# key_condition = f'pk = {pk} and {user_provided_sort} >= :start'
# Safe: use allowlist and placeholders
sort_field = 'created_at'
key_condition = 'pk = :pk and #sk >= :start'
expression_attribute_names = {'#sk': sort_field}
expression_attribute_values = {':pk': pk, ':start': '2024-01-01T00:00:00Z'}
response = table.query(
KeyConditionExpression=key_condition,
ExpressionAttributeNames=expression_attribute_names,
ExpressionAttributeValues=expression_attribute_values
)
4. When using higher-level abstractions, ensure they do not silently concatenate field names. Validate any dynamic component and prefer static definitions where possible.
These practices reduce the risk of injection-like behavior by ensuring that user input never directly shapes the structure of DynamoDB expressions, while still enabling flexible querying within safe boundaries.