Nosql Injection in Dynamodb
How Nosql Injection Manifests in Dynamodb
Nosql Injection in DynamoDB exploits how the service interprets query parameters. Unlike SQL injection where attackers manipulate SQL syntax, DynamoDB injection targets the query language's object structure and operators.
The most common attack vector involves manipulating FilterExpression, KeyConditionExpression, and ExpressionAttributeValues parameters. DynamoDB's flexible schema allows attackers to inject additional operators or modify existing ones through carefully crafted input.
Consider this vulnerable code pattern:
const userId = req.query.userId; // User-controlled input
const params = {
TableName: 'Users',
KeyConditionExpression: 'userId = :userId',
ExpressionAttributeValues: {
':userId': userId
}
};
const result = await dynamodb.query(params).promise();
An attacker could supply: userId=123 OR attribute_exists(password) to retrieve all items where userId equals 123 OR any item with a password attribute. The injected OR operator bypasses intended filtering.
Another common pattern exploits DynamoDB's between operator:
const startId = req.query.startId;
const endId = req.query.endId;
const params = {
TableName: 'Orders',
KeyConditionExpression: 'orderId between :start and :end',
ExpressionAttributeValues: {
':start': startId,
':end': endId
}
};
If an attacker supplies startId=1 and endId=9999 OR 1=1, the query becomes orderId between 1 and 9999 OR 1=1, which evaluates to true for all items.
Attribute value substitution also presents risks. DynamoDB uses placeholders like :value that get replaced in expressions. If input isn't properly sanitized, attackers can manipulate the structure:
// Vulnerable
const status = req.query.status;
const params = {
TableName: 'Orders',
FilterExpression: 'contains(status, :status)',
ExpressionAttributeValues: {
':status': status
}
};
// Malicious input: ' OR contains(status, 'admin' OR 1=1
// Transforms to: contains(status, '' OR contains(status, 'admin' OR 1=1')
The attack succeeds because DynamoDB evaluates the entire expression, not just the substituted value.
Dynamodb-Specific Detection
Detecting NoSQL injection in DynamoDB requires understanding how the service processes expressions and validates input. Traditional SQL injection detection tools don't work because DynamoDB uses a different query language.
middleBrick's DynamoDB scanning specifically tests for injection vulnerabilities by:
- Analyzing
ExpressionAttributeValuesfor unescaped special characters that could modify query logic - Testing
FilterExpressionandKeyConditionExpressionwith operator injection payloads - Verifying proper escaping of attribute names in
ExpressionAttributeNames - Checking for hardcoded or predictable attribute values that could be manipulated
Common injection patterns middleBrick detects include:
| Injection Type | Payload Example | Effect |
|---|---|---|
| Operator Injection | 123 OR 1=1 |
Bypasses filtering |
| Attribute Manipulation | name OR attribute_exists(password) |
Access unauthorized attributes |
| Logical Bypass | anything OR true |
Always returns true |
| Range Manipulation | 1 AND 1=1 |
Modifies range queries |
Manual detection techniques include:
# Test for operator injection
curl -X POST https://api.example.com/orders \
-H "Content-Type: application/json" \
-d '{"startId":"1","endId":"9999 OR 1=1"}'
# Test for attribute manipulation
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"userId":"123 OR attribute_exists(admin_role)"}'
Look for responses that return more data than expected or error messages that reveal query structure. DynamoDB typically returns generic errors, but timing differences can indicate successful injection attempts.
Dynamodb-Specific Remediation
Remediating NoSQL injection in DynamoDB requires a defense-in-depth approach using the service's built-in security features and proper input validation.
1. Strict Input Validation
const { isSafeString, isSafeNumber } = require('./input-sanitizer');
function validateUserId(userId) {
if (!isSafeString(userId) || userId.includes(' ') || userId.includes(';')) {
throw new Error('Invalid userId format');
}
return userId;
}
function validateOrderId(orderId) {
const orderIdNum = Number(orderId);
if (isNaN(orderIdNum) || orderIdNum < 0 || orderIdNum > 999999) {
throw new Error('Invalid orderId range');
}
return orderIdNum;
}
// Usage
const userId = validateUserId(req.query.userId);
const orderId = validateOrderId(req.query.orderId);
2. Parameterized Queries with Whitelisting
const validOperators = ['=', 'between', '>', '<', '>=', '<='];
function createSafeQuery(table, keyCondition, values) {
const [attr, operator, value] = keyCondition.split(' ');
if (!validOperators.includes(operator)) {
throw new Error('Invalid operator');
}
const safeValues = {};
Object.keys(values).forEach(key => {
if (typeof values[key] === 'string') {
safeValues[key] = values[key].replace(/["';]/g, '');
} else {
safeValues[key] = values[key];
}
});
return {
TableName: table,
KeyConditionExpression: keyCondition,
ExpressionAttributeValues: safeValues
};
}
// Usage
const params = createSafeQuery(
'Orders',
'orderId between :start and :end',
{ ':start': startId, ':end': endId }
);
3. IAM Policy Restrictions
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:Query",
"dynamodb:Scan"
],
"Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/Orders",
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:Attributes": ["orderId", "userId", "status"]
}
}
}
]
}
This policy restricts queries to specific attributes, preventing injection of arbitrary attribute names.
4. Using DynamoDB's Built-in Protection
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB.DocumentClient();
// Use strongly typed inputs
function getOrdersByUser(userId, limit = 100) {
if (typeof userId !== 'string' || userId.length > 255) {
throw new Error('Invalid userId');
}
if (typeof limit !== 'number' || limit < 1 || limit > 1000) {
throw new Error('Invalid limit');
}
const params = {
TableName: 'Orders',
KeyConditionExpression: 'userId = :userId',
ExpressionAttributeValues: {
':userId': userId
},
Limit: limit
};
return dynamodb.query(params).promise();
}
5. Regular Security Scanning
Integrate middleBrick into your CI/CD pipeline to automatically scan DynamoDB endpoints:
# .github/workflows/dynamodb-security.yml
name: DynamoDB Security Scan
on:
pull_request:
paths: ['src/dynamodb/**']
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Scan DynamoDB endpoints
run: |
npm install -g middlebrick
middlebrick scan https://api.example.com/dynamodb/query
This ensures NoSQL injection vulnerabilities are caught before deployment.