2023-03-07 17:16:47 +01:00
|
|
|
== How to fix it in Amazon DynamoDB
|
|
|
|
|
|
|
|
=== Code examples
|
2022-11-11 14:32:59 +01:00
|
|
|
|
|
|
|
The following code is vulnerable to NoSQL injection because untrusted data is
|
|
|
|
concatenated to the `FilterExpression` value. This expression determines which items within
|
|
|
|
the results should be returned.
|
|
|
|
|
|
|
|
A malicious HTTP request containing the following
|
|
|
|
query parameter values `username=admin&password=size(password) or
|
|
|
|
size(password)=size(password)` would allow an attacker to manipulate the returned data and bypass authentication.
|
|
|
|
|
|
|
|
==== Noncompliant code example
|
|
|
|
|
|
|
|
[source,python,diff-id=1,diff-type=noncompliant]
|
|
|
|
----
|
|
|
|
@app.route('/login')
|
|
|
|
def login():
|
|
|
|
dynamodb = AWS_SESSION.client('dynamodb')
|
|
|
|
|
|
|
|
username = request.args["username"]
|
|
|
|
password = request.args["password"]
|
|
|
|
|
|
|
|
dynamodb.scan(
|
|
|
|
FilterExpression= "username = " + username + " and password = " + password, # Noncompliant
|
|
|
|
TableName="users",
|
|
|
|
ProjectionExpression="username"
|
|
|
|
)
|
|
|
|
----
|
|
|
|
|
|
|
|
==== Compliant solution
|
|
|
|
|
|
|
|
[source,python,diff-id=1,diff-type=compliant]
|
|
|
|
----
|
|
|
|
@app.route('/login')
|
|
|
|
def login():
|
|
|
|
dynamodb = AWS_SESSION.client('dynamodb')
|
|
|
|
|
|
|
|
username = request.args["username"]
|
|
|
|
password = request.args["password"]
|
|
|
|
|
|
|
|
dynamodb.query(
|
|
|
|
KeyConditionExpression= "username = :u",
|
|
|
|
FilterExpression= "password = :p",
|
|
|
|
ExpressionAttributeValues={
|
|
|
|
":u": { 'S': username },
|
|
|
|
":p": { 'S': password }
|
|
|
|
},
|
|
|
|
TableName="users",
|
|
|
|
ProjectionExpression="username"
|
|
|
|
)
|
|
|
|
----
|
|
|
|
|
|
|
|
=== How does this work?
|
|
|
|
|
|
|
|
As a rule of thumb, the approach to protect against injection vulnerabilities
|
|
|
|
is to ensure that untrusted data cannot break out of the initially intended
|
|
|
|
logic.
|
|
|
|
|
|
|
|
When using DynamoDB with Boto3, the best way to do so is by using expression
|
|
|
|
attributes as placeholders (`:placeholder`). It will end up replacing the attribute with
|
|
|
|
the value defined in `ExpressionAttributeValues` and prevent any alteration of the
|
|
|
|
original query logic. The compliant code example uses such an approach.
|
|
|
|
|
|
|
|
When possible, use the method `query` over `scan` as it disallows the `OR`
|
|
|
|
operator on the `KeyConditionExpression` attribute and therefore reduces the attack
|
|
|
|
surface. It also optimizes speed and costs.
|
|
|
|
|
|
|
|
This logic applies both when using the `DynamoDB.Client` and the `DynamoDB.Table` class, though
|
|
|
|
the syntax differs for the latter, and the `ExpressionAttributeValues` would look
|
|
|
|
like the following:
|
|
|
|
|
|
|
|
[source,python]
|
|
|
|
----
|
|
|
|
ExpressionAttributeValues={
|
|
|
|
":u": username,
|
|
|
|
":p": password
|
|
|
|
}
|
|
|
|
----
|
|
|
|
|
|
|
|
Although injection can occur on all the query or scan `Expression` attributes,
|
2023-05-25 14:18:12 +02:00
|
|
|
its most severe impact occurs in the `FilterExpression`.
|