Lambda SQL Injection via psycopg2 cursor.execute()

CRITICAL

Lambda event data flows to psycopg2 cursor.execute() without parameterization, enabling SQL injection against RDS PostgreSQL or Aurora PostgreSQL backends.

Rule Information

Language
Python
Category
AWS Lambda
Author
Shivasurya
Shivasurya
Last Updated
2026-03-22
Tags
pythonawslambdasql-injectionpsycopg2postgresqlrdsaurorataint-analysisinter-proceduralCWE-89OWASP-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-011 --project .
1
2
3
4
5
6
7
8
9
10
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
37
38
39
40
41
42
43

About This Rule

Understanding the vulnerability and how it is detected

This rule detects SQL injection vulnerabilities in AWS Lambda functions where untrusted event data flows into psycopg2 cursor.execute() calls without proper parameterization, enabling SQL injection against RDS PostgreSQL, Aurora PostgreSQL, or any PostgreSQL-compatible backend.

Lambda functions frequently connect to RDS PostgreSQL or Aurora PostgreSQL using psycopg2, the standard Python PostgreSQL adapter. Event data from API Gateway, SQS, SNS, S3, and other triggers (event.get("body"), event.get("queryStringParameters"), event["Records"]) is attacker-controllable and must not be embedded in SQL strings.

When event data is concatenated or f-stringed into the SQL query string before being passed as the first argument to cursor.execute(), an attacker can inject arbitrary SQL. PostgreSQL is particularly powerful as an injection target: attackers can use COPY TO/FROM PROGRAM to execute OS commands (on unmanaged PostgreSQL; RDS restricts this but not all extensions), pg_read_file() to read server files, and UNION SELECT to exfiltrate arbitrary tables. Lambda functions connecting to PostgreSQL typically lack the ORM layer that encourages parameterization, making raw psycopg2 calls a common source of injection vulnerabilities.

Security Implications

Potential attack scenarios if this vulnerability is exploited

1

Full Database Exfiltration via UNION SELECT

An attacker who controls any SQL fragment can use UNION SELECT to read from any table accessible to the Lambda's database user, including user credentials, session tokens, PII, and financial records stored in the RDS PostgreSQL database.

2

PostgreSQL Extension Abuse

PostgreSQL supports extensions that can be leveraged via SQL injection. dblink and postgres_fdw can be used to connect to other database servers. Large object functions (lo_import, lo_export) can read and write server files on unmanaged PostgreSQL. Even on RDS where these are restricted, schema enumeration and data exfiltration remain fully possible.

3

Authentication and Authorization Bypass

Lambda functions that validate user credentials or check authorization via SQL queries are vulnerable to ' OR '1'='1 style bypasses that cause the query to return all rows. This can grant attackers administrative access or allow them to act as any user in the system.

4

Second-Order Injection via Stored Data

Injection payloads stored in the database by one Lambda function can be triggered by a different Lambda function that reads the data and passes it to another cursor.execute() without re-parameterizing. This cross-function second-order injection is harder to detect and requires end-to-end taint tracking.

How to Fix

Recommended remediation steps

  • 1Always pass Lambda event data as the second argument to cursor.execute() as a tuple, never by concatenating it into the SQL string.
  • 2Use psycopg2's %s placeholders for all parameterized queries; never use Python string formatting (%, .format(), f-strings) to build SQL strings.
  • 3Grant the Lambda's PostgreSQL user the minimum necessary privileges (CONNECT, SELECT on specific tables) and avoid SUPERUSER or CREATEROLE grants.
  • 4Enable RDS PostgreSQL's pg_stat_activity logging and set log_min_duration_statement to detect slow or unusual queries that may indicate injection.
  • 5Use RDS Proxy for connection pooling to prevent connection exhaustion from Lambda's per-invocation connection model.

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("*.execute") matching psycopg2 cursor objects with the tainted SQL string tracked via .tracks(0) (the query string argument). Sanitizers include explicit int() or float() type conversion. The analysis follows taint through string concatenation, f-string interpolation, variable assignments, and function boundaries.

Compliance & Standards

Industry frameworks and regulations that require detection of this vulnerability

CWE Top 25
CWE-89 ranked #3 in 2023 Most Dangerous Software Weaknesses
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 database permissions

References

External resources and documentation

Similar Rules

Explore related security rules for Python

Frequently Asked Questions

Common questions about Lambda SQL Injection via psycopg2 cursor.execute()

psycopg2 implements the Python DB-API 2.0 with 'pyformat' paramstyle, using %s as the placeholder regardless of the actual SQL parameter type. The %s is not Python string formatting; it is a psycopg2-specific placeholder that is replaced by the driver with properly escaped values. Never confuse this with Python's %-string formatting, which would introduce injection risk. Always pass values as the second tuple argument to cursor.execute(), not via Python string formatting.
Both are safe when values are passed as separate parameters, not embedded in the SQL string. execute_values() takes a SQL template with %s placeholders and a sequence of value tuples. As long as the SQL template is a static string and event data appears only in the values sequence, injection is prevented. Never build the SQL template string from event data.
Column names and ORDER BY directions cannot be parameterized in SQL. Use an explicit allowlist: define a set of permitted column names and sort directions, validate the event value against the allowlist, and use the validated value to select from a pre-built dictionary of safe SQL fragments. Never interpolate event data directly into column name or ORDER BY positions.
Amazon RDS restricts the most dangerous superuser-level operations. COPY TO/FROM PROGRAM (server-side OS execution) is not available on RDS. However, many other injection impacts remain fully exploitable on RDS: UNION SELECT for data exfiltration, stacked queries for data modification, and schema enumeration via information_schema. Parameterization is always required regardless of the database environment.
Initialize the database connection outside the lambda_handler() function at module level, and reuse it across warm invocations. Test the connection before use and reconnect if it has been closed. For high-concurrency Lambda functions, use RDS Proxy to pool connections, as Lambda's scaling model can exhaust PostgreSQL's connection limits quickly.

New feature

Get these findings posted directly on your GitHub pull requests

The Lambda SQL Injection via psycopg2 cursor.execute() rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.

See how it works