Cross Site Request Forgery in Django with Dynamodb
Cross Site Request Forgery in Django with Dynamodb — how this specific combination creates or exposes the vulnerability
Cross Site Request Forgery (CSRF) is an attack that tricks a logged-in user into executing unwanted actions on a web application in which they are authenticated. In a Django application using Amazon DynamoDB as the data store, CSRF risk arises from the interaction between Django’s session- and cookie-based authentication and DynamoDB operations that mutate state. Even though DynamoDB itself does not provide web session constructs, Django’s server-side session handling (e.g., session key in a cookie) identifies the user, and DynamoDB is used to read and write application data. If views that perform destructive actions (such as updating or deleting items) rely only on cookies/sessions for identity and do not verify the request origin, an attacker can craft a malicious site that issues requests on behalf of the victim.
DynamoDB does not have built-in CSRF protections; it enforces authentication and authorization at the AWS account and IAM level. In this architecture, the application layer (Django) is responsible for ensuring that requests are intentional. A typical exposure occurs when a Django view directly executes a DynamoDB put_item or update_item based on user-supplied parameters without validating the HTTP referrer or including anti-CSRF tokens. For example, an endpoint that updates user preferences in DynamoDB might accept a POST with JSON body parameters and apply them using the AWS SDK, trusting the Django session to identify the user. Because session cookies are automatically included by browsers in cross-origin requests, an attacker can trick the victim’s browser into submitting such a POST, causing unauthorized DynamoDB writes.
The risk is compounded when developers assume DynamoDB’s IAM policies alone prevent CSRF. IAM policies govern who (which role or user) can call DynamoDB APIs, but they do not distinguish between a legitimate request from the application backend and a forged request sent via the user’s browser. Therefore, the burden of request validation falls entirely on Django. Missing or weak CSRF defenses in Django (such as not using CsrfViewMiddleware or not including the CSRF token in forms and AJAX requests) combined with DynamoDB as the backend create a practical path for unauthorized state changes, including updating items, deleting records, or invoking other data-modifying operations that the user did not intend.
Real-world scenarios include a banking-like API where a user’s balances are stored in DynamoDB and updated via Django views. If a transfer endpoint does not validate the origin of the request, an attacker could host a page with an image tag or form that triggers a balance update via a crafted URL or script, leveraging the victim’s authenticated session. Because the scan categories include Authentication and BOLA/IDOR, a tool like middleBrick can surface missing CSRF protections when it detects state-changing endpoints that do not enforce token validation, providing prioritized findings with severity and remediation guidance.
Whether using the CLI (middlebrick scan <url>) or the Web Dashboard, teams can track these findings over time. The GitHub Action can be added to CI/CD pipelines to fail builds if security scores drop below a chosen threshold, while the MCP Server enables scanning APIs directly from AI coding assistants. Note that middleBrick detects and reports these risks but does not fix, patch, or block; it provides remediation guidance to guide developers in applying appropriate controls.
Dynamodb-Specific Remediation in Django — concrete code fixes
To mitigate CSRF when Django interacts with DynamoDB, enforce anti-CSRF tokens on all state-changing views and ensure proper token validation before constructing any DynamoDB request. Django provides built-in CSRF protection via CsrfViewMiddleware and the {% csrf_token %} template tag. For API endpoints, include the CSRF token in request headers (e.g., X-CSRFToken) and validate it on the server. Below are concrete examples that combine DynamoDB operations with proper CSRF defenses.
- Ensure CSRF middleware is enabled (it is by default in Django settings) and decorate state-changing views with
csrf_protectif needed:
from django.views.decorators.csrf import csrf_protect
from django.http import JsonResponse
import boto3
from botocore.exceptions import ClientError
@csrf_protect
def update_user_preferences(request):
if request.method != 'POST':
return JsonResponse({'error': 'Method not allowed'}, status=405)
# Django verifies CSRF token automatically when using templates and forms.
# For AJAX, ensure the token is sent in a header and verified via ensure_csrf_cookie
# or by relying on CsrfViewMiddleware.
user_id = request.POST.get('user_id')
preference_key = request.POST.get('preference_key')
preference_value = request.POST.get('preference_value')
if not all([user_id, preference_key, preference_value]):
return JsonResponse({'error': 'Missing parameters'}, status=400)
# Perform the DynamoDB update safely after CSRF validation
dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
table = dynamodb.Table('UserPreferences')
try:
table.update_item(
Key={'user_id': user_id},
UpdateExpression='SET #pref = :val',
ExpressionAttributeNames={'#pref': preference_key},
ExpressionAttributeValues={':val': preference_value}
)
except ClientError as e:
return JsonResponse({'error': str(e)}, status=500)
return JsonResponse({'status': 'updated'})
- For JSON payloads, ensure the CSRF token is included in the header and that Django’s
csrf_exemptis not used on sensitive endpoints:
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
# Do not use csrf_exempt on state-changing endpoints.
# Instead, rely on CSRF token in the HTTP header.
import boto3
from django.views.decorators.http import require_POST
from django.middleware.csrf import get_token
@require_POST
def delete_item(request):
# The view expects the CSRF token in the 'X-CSRFToken' header.
# Middleware will reject the request if the token is missing or invalid.
item_id = request.POST.get('item_id')
if not item_id:
return JsonResponse({'error': 'item_id required'}, status=400)
dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
table = dynamodb.Table('Items')
try:
table.delete_item(Key={'id': item_id})
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
return JsonResponse({'status': 'deleted'})
- When using AJAX from a browser, include the CSRF token in the request header. Django’s
get_tokencan be used to embed the token in the page, and JavaScript can read it and send it in headers:
# In your Django template:
# <script>var csrftoken = '{{ csrf_token }}';</script>
# Then in your JavaScript fetch call:
# fetch('/api/delete-item/', {
# method: 'POST',
# headers: {
# 'Content-Type': 'application/json',
# 'X-CSRFToken': csrftoken
# },
# body: JSON.stringify({item_id: '123'})
# });
- For higher assurance, pair CSRF tokens with same-site cookie attributes and secure headers. Configure Django settings to enforce strict referrer policies and ensure that DynamoDB calls are made only after successful token validation. middleBrick’s scans can highlight endpoints that lack CSRF defenses and map findings to frameworks such as OWASP API Top 10 and PCI-DSS, helping prioritize remediation.