DynamoDB
概要
IAM Access Analyzer が DynamoDB テーブルおよびストリームの外部アクセスをどのように検出するかを検証する。
DynamoDB テーブルとストリームはリソースベースポリシー(2024 年 3 月サポート開始)により外部アクセスを制御できる。デフォルトではリソースベースポリシーが存在せず、IAM ポリシーで明示的にアクセスを許可されたアカウント内のプリンシパルのみがアクセスできる。DynamoDB には Block Public Access (BPA) が常時有効で無効化できないため、Principal: "*"(Condition なし)のパブリックポリシーは設定自体がブロックされる。Principal: {"AWS": "*"} であっても Condition で aws:PrincipalAccount や aws:SourceVpc などの条件キーを指定してアクセス元を限定すれば BPA を通過するが、この場合 IAM Access Analyzer はパブリックとは判定しない。特定の外部アカウントを指定したクロスアカウントポリシーは設定可能で、IAM Access Analyzer はクロスアカウントアクセスとして検出する。
DynamoDB の外部アクセス制御レイヤー
| レイヤー | DynamoDB の状況 |
|---|---|
| デフォルト保護 | Block Public Access (BPA) が常時有効(無効化不可) |
| 予防(SCP / RCP) | Control Tower 予防コントロールなし |
| Security Hub CSPM | 外部アクセスチェックなし |
| IAM Access Analyzer | クロスアカウントの到達可能性を分析(パブリックは BPA によりブロック) |
結果
- デフォルト保護: DynamoDB には BPA が常時有効で、
Principal: {"AWS": "*"}(Condition なし)のパブリックポリシーはValidationExceptionで設定がブロックされる。テーブル・ストリーム両方で確認 - パブリックアクセスの検出: BPA によりパブリックポリシーの設定自体がブロックされるため、パブリックアクセスの finding は生成されない
- Condition 付きポリシー:
Principal: {"AWS": "*"}+Condition: {"StringEquals": {"aws:PrincipalAccount": "<アカウント ID>"}}は BPA を通過する。IAM Access Analyzer はisPublic: falseのクロスアカウント finding を生成した - クロスアカウントアクセスの検出(テーブル): テーブルのリソースベースポリシーに組織外アカウント ID を設定すると
isPublic: falseの finding が生成された - クロスアカウントアクセスの検出(ストリーム): ストリームのリソースベースポリシーに組織外アカウント ID を設定すると
isPublic: falseの finding が生成された - resourceControlPolicyRestriction: DynamoDB に対応する RCP 型予防コントロールは存在しないため、すべて
NOT_APPLICABLE - ポリシー削除による finding の解消:
delete-resource-policyでリソースベースポリシーを削除すると finding は RESOLVED になった
検証環境
本記事のコマンドは、--profile 指定がない場合は Workload アカウントで実行する。IAM Access Analyzer の finding 確認は Audit アカウントで実行する。
検証の流れ
flowchart LR
A[1. 既存 finding の<br>確認] --> B[2. デフォルト設定<br>の確認]
B --> C[3. BPA の確認<br>Condition 付き<br>ポリシーの検証]
C --> D[4. クロスアカウント<br>アクセスの検出<br>テーブル]
D --> E[5. クロスアカウント<br>アクセスの検出<br>ストリーム]
E --> F[6. クリーンアップ]
1. 既存 finding の確認
テスト用テーブルを作成する前に、既存の DynamoDB finding を確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::DynamoDB::Table"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Audit[]aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::DynamoDB::Stream"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Audit[]2. デフォルト設定の確認
テスト用テーブルを作成し、デフォルトではリソースベースポリシーが存在しないことを確認する。
テスト用テーブルの作成
aws dynamodb create-table \
--table-name iaa-dynamodb-test \
--attribute-definitions AttributeName=pk,AttributeType=S \
--key-schema AttributeName=pk,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--query '{TableName:TableDescription.TableName,TableStatus:TableDescription.TableStatus,TableArn:TableDescription.TableArn}'{
"TableName": "iaa-dynamodb-test",
"TableStatus": "CREATING",
"TableArn": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test"
}デフォルトのリソースベースポリシー
aws dynamodb get-resource-policy \
--resource-arn arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-testAn error occurred (PolicyNotFoundException) when calling the GetResourcePolicy operation: Resource-based policy not found for the provided ResourceArn: arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-testデフォルトではリソースベースポリシーが存在しない。
デフォルト状態での組織外アクセス確認
リソースベースポリシーが存在しない状態で、組織外アカウントからアクセスを試みる。クロスアカウントアクセスでは --table-name にテーブル ARN を指定する必要がある(テーブル名のみの場合はリクエスタ自身のアカウントのテーブルを参照する)。
aws dynamodb get-item \
--table-name arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test \
--key '{"pk": {"S": "test"}}' \
--profile <組織外プロファイル> \
--region ap-northeast-1An error occurred (AccessDeniedException) when calling the GetItem operation: User: arn:aws:iam::<組織外アカウント ID>:user/<組織外ユーザー名> is not authorized to perform: dynamodb:GetItem on resource: arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test because no resource-based policy allows the dynamodb:GetItem actionリソースベースポリシーがない状態では組織外からのアクセスは拒否される。
3. BPA の確認
テーブルへのパブリックポリシー設定(ブロックされる)
Principal: "*" の形式で設定を試みる。
aws dynamodb put-resource-policy \
--resource-arn arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "dynamodb:GetItem",
"Resource": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test"
}
]
}'An error occurred (ValidationException) when calling the PutResourcePolicy operation: Resource-based policy grants unbounded access in one or more elements. Please revise the policy to ensure least-privilege form of accessPrincipal: {"AWS": "*"} の形式でも同様にブロックされることを確認する。
aws dynamodb put-resource-policy \
--resource-arn arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicAccess",
"Effect": "Allow",
"Principal": {"AWS": "*"},
"Action": "dynamodb:GetItem",
"Resource": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test"
}
]
}'An error occurred (ValidationException) when calling the PutResourcePolicy operation: Resource-based policy grants unbounded access in one or more elements. Please revise the policy to ensure least-privilege form of accessBPA により Principal: "*" および Principal: {"AWS": "*"}(いずれも Condition なし)のパブリックポリシーは設定がブロックされる。
Condition 付きポリシーの設定(BPA を通過する)
公式ドキュメントによると、Principal が "*" であっても Condition で aws:PrincipalAccount や aws:SourceVpc などの条件キーを指定してアクセス元を限定すれば「non-public」と判定され BPA を通過する。Principal: "*" と Principal: {"AWS": "*"} の両形式で確認する。
aws dynamodb put-resource-policy \
--resource-arn arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ConditionedAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "dynamodb:GetItem",
"Resource": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test",
"Condition": {
"StringEquals": {
"aws:PrincipalAccount": "<組織外アカウント ID>"
}
}
}
]
}'{
"RevisionId": "<リビジョン ID>"
}Principal: "*" + Condition で BPA を通過した。ポリシーを削除し、Principal: {"AWS": "*"} + Condition でも同様に確認する。
aws dynamodb delete-resource-policy \
--resource-arn arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test{
"RevisionId": "<リビジョン ID>"
}aws dynamodb put-resource-policy \
--resource-arn arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ConditionedAccess",
"Effect": "Allow",
"Principal": {"AWS": "*"},
"Action": "dynamodb:GetItem",
"Resource": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test",
"Condition": {
"StringEquals": {
"aws:PrincipalAccount": "<組織外アカウント ID>"
}
}
}
]
}'{
"RevisionId": "<リビジョン ID>"
}両形式とも BPA を通過した。この場合の IAM Access Analyzer の finding を確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::DynamoDB::Table"]}}' \
--query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
--region ap-northeast-1 \
--profile Audit[
{
"id": "<Condition 付き finding ID>",
"resource": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test",
"resourceType": "AWS::DynamoDB::Table",
"status": "ACTIVE"
}
]aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <Condition 付き finding ID> \
--region ap-northeast-1 \
--profile Audit{
"findingDetails": [
{
"externalAccessDetails": {
"action": [
"dynamodb:GetItem"
],
"condition": {
"aws:PrincipalAccount": "<組織外アカウント ID>"
},
"isPublic": false,
"principal": {"AWS": "*"},
"resourceControlPolicyRestriction": "NOT_APPLICABLE"
}
}
],
"resource": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test",
"status": "ACTIVE",
"error": null,
"createdAt": "<finding 作成日時>",
"resourceType": "AWS::DynamoDB::Table",
"findingType": "ExternalAccess",
"resourceOwnerAccount": "<Workload アカウント ID>",
"analyzedAt": "<分析日時>",
"id": "<Condition 付き finding ID>",
"updatedAt": "<finding 更新日時>"
}isPublic が false であり、IAM Access Analyzer はパブリックアクセスとは判定していない。principal は {"AWS": "*"} のままだが、condition に aws:PrincipalAccount が記録されており、Condition によりアクセス元が限定されていることが示されている。
Condition 付きポリシーを削除して次のステップに進む。
aws dynamodb delete-resource-policy \
--resource-arn arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test{
"RevisionId": "<リビジョン ID>"
}ストリームへのパブリックポリシー設定(ブロックされる)
ストリームでも同様に BPA が機能することを確認するため、テーブルにストリームを有効化する。
aws dynamodb update-table \
--table-name iaa-dynamodb-test \
--stream-specification '{"StreamEnabled":true,"StreamViewType":"NEW_AND_OLD_IMAGES"}' \
--query '{StreamSpecification:TableDescription.StreamSpecification,LatestStreamArn:TableDescription.LatestStreamArn}'{
"StreamSpecification": {
"StreamEnabled": true,
"StreamViewType": "NEW_AND_OLD_IMAGES"
},
"LatestStreamArn": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test/stream/<ストリームタイムスタンプ>"
}aws dynamodb put-resource-policy \
--resource-arn arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test/stream/<ストリームタイムスタンプ> \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "dynamodb:GetRecords",
"Resource": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test/stream/<ストリームタイムスタンプ>"
}
]
}'An error occurred (ValidationException) when calling the PutResourcePolicy operation: Resource-based policy grants unbounded access in one or more elements. Please revise the policy to ensure least-privilege form of accessaws dynamodb put-resource-policy \
--resource-arn arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test/stream/<ストリームタイムスタンプ> \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicAccess",
"Effect": "Allow",
"Principal": {"AWS": "*"},
"Action": "dynamodb:GetRecords",
"Resource": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test/stream/<ストリームタイムスタンプ>"
}
]
}'An error occurred (ValidationException) when calling the PutResourcePolicy operation: Resource-based policy grants unbounded access in one or more elements. Please revise the policy to ensure least-privilege form of accessストリームでも BPA により両形式のパブリックポリシーの設定がブロックされる。
4. クロスアカウントアクセスの検出(テーブル)
テーブルにクロスアカウントアクセスを許可するリソースベースポリシーを設定する。
aws dynamodb put-resource-policy \
--resource-arn arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CrossAccountAccess",
"Effect": "Allow",
"Principal": {"AWS": "<組織外アカウント ID>"},
"Action": "dynamodb:GetItem",
"Resource": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test"
}
]
}'{
"RevisionId": "<リビジョン ID>"
}テーブルの finding を確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::DynamoDB::Table"]}}' \
--query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
--region ap-northeast-1 \
--profile Audit[
{
"id": "<テーブル finding ID>",
"resource": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test",
"resourceType": "AWS::DynamoDB::Table",
"status": "ACTIVE"
}
]finding の詳細を確認する。
aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <テーブル finding ID> \
--region ap-northeast-1 \
--profile Audit{
"findingDetails": [
{
"externalAccessDetails": {
"action": [
"dynamodb:GetItem"
],
"condition": {},
"isPublic": false,
"principal": {"AWS": "<組織外アカウント ID>"},
"resourceControlPolicyRestriction": "NOT_APPLICABLE"
}
}
],
"resource": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test",
"status": "ACTIVE",
"error": null,
"createdAt": "<finding 作成日時>",
"resourceType": "AWS::DynamoDB::Table",
"findingType": "ExternalAccess",
"resourceOwnerAccount": "<Workload アカウント ID>",
"analyzedAt": "<分析日時>",
"id": "<テーブル finding ID>",
"updatedAt": "<finding 更新日時>"
}以下を確認する。
isPublicがfalseprincipalに組織外アカウント ID が特定されているactionにdynamodb:GetItemが含まれるresourceControlPolicyRestrictionがNOT_APPLICABLE
組織外アカウントからの実際のアクセス確認
組織外アカウントからテーブルへの GetItem が成功することを確認する。リソースベースポリシーが設定されていない場合は AccessDeniedException になるため、レスポンスが返ればアクセスが許可されていることを意味する。クロスアカウントアクセスでは --table-name にテーブル ARN を指定する。
aws dynamodb get-item \
--table-name arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test \
--key '{"pk": {"S": "test"}}' \
--profile <組織外プロファイル> \
--region ap-northeast-1
(出力なし)アイテムが存在しないため出力は空だが、AccessDeniedException にならずレスポンスが返ったことでアクセスが許可されていることを確認できた。
5. クロスアカウントアクセスの検出(ストリーム)
ストリームにクロスアカウントアクセスを許可するリソースベースポリシーを設定する。
aws dynamodb put-resource-policy \
--resource-arn arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test/stream/<ストリームタイムスタンプ> \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CrossAccountAccess",
"Effect": "Allow",
"Principal": {"AWS": "<組織外アカウント ID>"},
"Action": "dynamodb:DescribeStream",
"Resource": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test/stream/<ストリームタイムスタンプ>"
}
]
}'{
"RevisionId": "<リビジョン ID>"
}ストリームの finding を確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::DynamoDB::Stream"]}}' \
--query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
--region ap-northeast-1 \
--profile Audit[
{
"id": "<ストリーム finding ID>",
"resource": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test/stream/<ストリームタイムスタンプ>",
"resourceType": "AWS::DynamoDB::Stream",
"status": "ACTIVE"
}
]finding の詳細を確認する。
aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <ストリーム finding ID> \
--region ap-northeast-1 \
--profile Audit{
"findingDetails": [
{
"externalAccessDetails": {
"action": [
"dynamodb:DescribeStream"
],
"condition": {},
"isPublic": false,
"principal": {"AWS": "<組織外アカウント ID>"},
"resourceControlPolicyRestriction": "NOT_APPLICABLE"
}
}
],
"resource": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test/stream/<ストリームタイムスタンプ>",
"status": "ACTIVE",
"error": null,
"createdAt": "<finding 作成日時>",
"resourceType": "AWS::DynamoDB::Stream",
"findingType": "ExternalAccess",
"resourceOwnerAccount": "<Workload アカウント ID>",
"analyzedAt": "<分析日時>",
"id": "<ストリーム finding ID>",
"updatedAt": "<finding 更新日時>"
}以下を確認する。
isPublicがfalseprincipalに組織外アカウント ID が特定されているactionにdynamodb:DescribeStreamが含まれるresourceControlPolicyRestrictionがNOT_APPLICABLE
組織外アカウントからの実際のアクセス確認
組織外アカウントからストリームへの DescribeStream が成功することを確認する。
aws dynamodbstreams describe-stream \
--stream-arn arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test/stream/<ストリームタイムスタンプ> \
--query '{StreamArn:StreamDescription.StreamArn,StreamStatus:StreamDescription.StreamStatus,TableName:StreamDescription.TableName}' \
--profile <組織外プロファイル> \
--region ap-northeast-1{
"StreamArn": "arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test/stream/<ストリームタイムスタンプ>",
"StreamStatus": "ENABLED",
"TableName": "iaa-dynamodb-test"
}6. クリーンアップ
リソースベースポリシーの削除
aws dynamodb delete-resource-policy \
--resource-arn arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test{
"RevisionId": "<リビジョン ID>"
}aws dynamodb delete-resource-policy \
--resource-arn arn:aws:dynamodb:ap-northeast-1:<Workload アカウント ID>:table/iaa-dynamodb-test/stream/<ストリームタイムスタンプ>{
"RevisionId": "<リビジョン ID>"
}finding の確認
ポリシー削除後、finding が RESOLVED になることを確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::DynamoDB::Table"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Audit[]aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::DynamoDB::Stream"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Audit[]テスト用テーブルの削除
aws dynamodb delete-table \
--table-name iaa-dynamodb-test \
--query '{TableName:TableDescription.TableName,TableStatus:TableDescription.TableStatus}'{
"TableName": "iaa-dynamodb-test",
"TableStatus": "DELETING"
}