Lambda DynamoDB FilterExpression Injection

HIGH

Lambda event data flows to a DynamoDB FilterExpression string, enabling expression manipulation in serverless DynamoDB scan and query operations.

Rule Information

Language
Python
Category
AWS Lambda
Author
Shivasurya
Shivasurya
Last Updated
2026-03-22
Tags
pythonawslambdadynamodbnosql-injectionfilter-expressionserverlesstaint-analysisinter-proceduralCWE-943OWASP-A03
CWE References

Interactive Playground

Experiment with the vulnerable code and security rule below. Edit the code to see how the rule detects different vulnerability patterns.

pathfinder scan --ruleset python/PYTHON-LAMBDA-SEC-016 --project .
1
2
3
4
5
6
7
8
9
rule.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

About This Rule

Understanding the vulnerability and how it is detected

This rule detects injection vulnerabilities unique to serverless DynamoDB integrations in AWS Lambda functions, where untrusted event data flows into a DynamoDB FilterExpression string passed to table.scan() or table.query() calls.

DynamoDB is the most commonly used database for AWS Lambda functions due to its serverless scaling model, IAM-based access control, and native AWS integration. When Lambda functions use the low-level DynamoDB API with raw expression strings (rather than the high-level boto3 Attr() and Key() condition builder), event data can be injected into the FilterExpression string parameter.

Unlike SQL injection, DynamoDB FilterExpression injection does not enable arbitrary data access (DynamoDB does not support cross-table operations or nested expression execution). However, attackers can manipulate filter logic to bypass intended access controls, cause denial-of-service by injecting always-false conditions that return empty results, enumerate attribute names via error-based probing, or modify scan behavior to expose data that should be filtered out.

This vulnerability is specific to the serverless ecosystem: Lambda functions commonly skip the extra abstraction layer that would prevent it, and DynamoDB's expression language is less widely understood than SQL, making developers less likely to recognize injection patterns.

Security Implications

Potential attack scenarios if this vulnerability is exploited

1

Filter Logic Bypass via Expression Injection

An attacker who controls part of the FilterExpression string can inject expression syntax to bypass intended data access controls. For example, injecting OR attribute_exists(id) into a filter intended to restrict results to a specific user's records can cause the filter to return all items in the table rather than only the intended subset.

2

Access Control Bypass in Multi-Tenant Data

DynamoDB tables in Lambda applications often store multi-tenant data separated by a tenant_id attribute. FilterExpression injection can bypass tenant isolation by modifying the filter to include OR conditions that match items from other tenants, exposing cross-tenant data to attackers.

3

Denial of Service via Always-False Filters

Injecting always-false conditions (e.g., attribute_not_exists(id) when id always exists) causes table.scan() to consume all DynamoDB read capacity units while returning no results. For large tables with high read capacity costs, this can generate significant AWS charges and degrade application performance.

4

Attribute Name Enumeration via Error Probing

DynamoDB returns specific error messages for invalid expression attribute names. By injecting probe expressions and observing error responses, an attacker can enumerate the attribute names present in the DynamoDB table, revealing the data schema for use in more targeted attacks.

How to Fix

Recommended remediation steps

  • 1Use boto3's Attr() and Key() condition expression builders instead of raw FilterExpression strings; the SDK handles value escaping and expression syntax automatically.
  • 2Always use ExpressionAttributeValues and ExpressionAttributeNames for substitution when FilterExpression strings must be constructed programmatically.
  • 3Validate event fields against strict allowlists before using them in any DynamoDB expression, particularly for attribute names that cannot be parameterized by the SDK.
  • 4Apply DynamoDB IAM policies that restrict the Lambda's access to specific tables and operations (e.g., GetItem, Query only; no Scan on production tables).
  • 5Prefer table.query() with KeyConditionExpression over table.scan() with FilterExpression; queries are more efficient and inherently scope to a partition key value.

Detection Scope

How Code Pathfinder analyzes your code for this vulnerability

This rule performs inter-procedural taint analysis with global scope. Sources are Lambda event dictionary access calls: calls("event.get"), calls("event.__getitem__"), including event.get("body"), event.get("queryStringParameters"), event.get("pathParameters"), and event["Records"]. The sink is calls("*.scan") and calls("*.query") on DynamoDB Table objects when the tainted value reaches the FilterExpression string argument (tracked via .tracks(0)). The rule specifically flags raw string FilterExpression arguments that contain event data, not Attr()-based condition expressions. The analysis follows taint through f-string interpolation, string concatenation, variable assignments, and function boundaries.

Compliance & Standards

Industry frameworks and regulations that require detection of this vulnerability

CWE Top 25
CWE-943 - Improper Neutralization of Special Elements in Data Query Logic
OWASP Top 10
A03:2021 - Injection
PCI DSS v4.0
Requirement 6.2.4 - protect against injection attacks
NIST SP 800-53
SI-10: Information Input Validation
AWS Security Best Practices
Validate all inputs; apply least-privilege DynamoDB IAM policies

References

External resources and documentation

Similar Rules

Explore related security rules for Python

Frequently Asked Questions

Common questions about Lambda DynamoDB FilterExpression Injection

DynamoDB FilterExpression injection is less severe than SQL injection because DynamoDB's expression language does not support cross-table operations, subqueries, OS commands, or file access. However, it can still enable data access control bypass (exposing rows that should be filtered), multi-tenant data leakage, and denial-of- service via always-false conditions that waste read capacity units. The impact is scoped to the table being queried.
FilterExpression as a raw string (e.g., "status = :s") requires manually specifying ExpressionAttributeValues (:s mapping) and is vulnerable to injection when event data is embedded in the expression string itself. Attr("status").eq(status) uses the boto3 condition builder, which generates the expression string and attribute value mapping internally. The value (status) is passed as a Python object and the SDK ensures it is properly represented as an ExpressionAttributeValue, preventing injection of expression syntax.
DynamoDB is the default database for Lambda functions due to its native AWS integration and serverless scaling. Unlike relational databases where ORMs are the standard abstraction, DynamoDB is often accessed via the low-level boto3 API directly in Lambda handlers, which requires developers to construct expression strings manually. The expression language is less familiar than SQL, making developers less likely to recognize injection risks when building filter strings from event data.
Yes, if done correctly. ExpressionAttributeValues safely substitutes placeholder values (e.g., :val) with actual values in the expression. ExpressionAttributeNames safely substitutes placeholder names (e.g., #attr) with actual attribute names. If both the expression structure and the values use this substitution mechanism rather than direct string embedding, injection is prevented. However, the boto3 Attr() builder is simpler and less error-prone for most use cases.
Build a list of Attr() conditions and combine them with the & operator: filter_expr = Attr('status').eq(status) & Attr('region').eq(region). For a variable number of conditions, start with the first Attr() condition and use reduce() or a loop to & additional conditions. This approach builds a complete parameterized expression using only boto3's condition builder and never embeds event data in expression strings.

New feature

Get these findings posted directly on your GitHub pull requests

The Lambda DynamoDB FilterExpression Injection rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.

See how it works