Insecure Deserialization in Django with Dynamodb
Insecure Deserialization in Django with DynamoDB — how this specific combination creates or exposes the vulnerability
Insecure deserialization occurs when an application accepts serialized data and reconstructs objects without integrity checks. In a Django application that uses DynamoDB as a persistence layer, this typically manifests when serialized objects (for example, Python objects pickled with pickle or complex structures serialized with JSON) are stored in DynamoDB and later retrieved and deserialized without validation.
DynamoDB itself is schema-less at the item level, which means developers can store nested structures and serialized blobs as attributes. If Django stores user-controlled data as serialized objects in DynamoDB and later deserializes them during request processing, an attacker can supply malicious payloads that lead to arbitrary code execution or sensitive data access upon deserialization. Common patterns include caching authenticated session data or complex model graphs as serialized blobs in DynamoDB and restoring them in views or middleware.
The combination increases risk because DynamoDB’s flexible attribute types can store deeply nested documents that may include serialized objects. If Django uses Python’s pickle to serialize model instances for storage and deserialization on retrieval, a malicious payload can exploit known gadget chains in the deserialization process. The unauthenticated attack surface of DynamoDB endpoints (when credentials are accidentally exposed or when using IAM roles too broadly) can also allow an attacker to tamper with stored items, replacing benign serialized data with malicious blobs.
Real-world attack patterns mirror the OWASP API Top 10 A08:2023 — Security Misconfiguration and A01:2021 — Broken Access Control. For example, an attacker might modify a DynamoDB item that contains a serialized object to include a payload that executes shell commands when the Django app deserializes it. Because DynamoDB does not enforce schema constraints, there is no native safeguard preventing such tampering if IAM permissions are misconfigured.
Because middleBrick tests unauthenticated attack surfaces and includes checks such as Input Validation, Data Exposure, and Unsafe Consumption, it can identify endpoints that accept or return serialized objects and flag insecure deserialization risks. Findings map to compliance frameworks including OWASP API Top 10 and can be tracked over time using the Web Dashboard or CLI reports.
DynamoDB-Specific Remediation in Django — concrete code fixes
Remediation focuses on avoiding unsafe deserialization of user-controlled data and enforcing strict validation when working with DynamoDB-stored objects. Do not use Python’s pickle to serialize data that may be stored in DynamoDB and later deserialized based on external input. Instead, use safe serialization formats such as JSON with defined schemas and validate all inputs before storage and after retrieval.
Below are concrete, safe patterns for storing and retrieving data in DynamoDB with Django-like abstractions, using the AWS SDK for Python (Boto3). These examples assume you have configured AWS credentials securely and are using DynamoDB resource interfaces.
Safe JSON-based storage and retrieval
import json
import boto3
from django.core.exceptions import ValidationError
dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
table = dynamodb.Table('api_items')
def store_item_safe(item_id: str, data: dict) -> None:
# Validate and serialize as JSON; do not use pickle
payload = json.dumps(data, separators=(',', ':'))
table.put_item(Item={
'item_id': item_id,
'payload': payload,
'content_type': 'application/json'
})
def retrieve_item_safe(item_id: str) -> dict:
response = table.get_item(Key={'item_id': item_id})
item = response.get('Item')
if not item:
raise ValidationError('Item not found')
if item.get('content_type') != 'application/json':
raise ValidationError('Unsupported content type')
return json.loads(item['payload'])
Schema validation with Pydantic (or Django model forms) before DynamoDB operations
from pydantic import BaseModel, ValidationError as PydanticValidationError
import boto3
import json
class ItemSchema(BaseModel):
name: str
value: int
tags: list[str] = []
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('api_items')
def store_with_schema(item_id: str, data: dict) -> None:
try:
validated = ItemSchema(**data)
except PydanticValidationError as e:
raise ValidationError(f'Invalid data: {e.errors()}')
table.put_item(Item={
'item_id': item_id,
'payload': json.dumps(validated.dict()),
'schema_version': '1'
})
def retrieve_with_schema(item_id: str) -> ItemSchema:
resp = table.get_item(Key={'item_id': item_id})
item = resp.get('Item')
if not item:
raise ValidationError('Item not found')
return ItemSchema(**json.loads(item['payload']))
Avoiding Dynamic Code Execution Patterns
- Never call functions or reconstruct classes based on deserialized type indicators that an attacker can control.
- If you must store complex model graphs, serialize only data and reconstruct instances using explicit mapping rather than generic deserialization.
- Ensure IAM policies for DynamoDB follow least privilege so that tampering with stored serialized objects is not trivially possible.
middleBrick’s LLM/AI Security checks and Input Validation tests can surface endpoints that rely on unsafe deserialization. Use the CLI (middlebrick scan <url>) or Web Dashboard to track findings and apply remediation. Pro plan continuous monitoring can help detect regressions as APIs evolve.