こんにちは。X(クロス)イノベーション本部 ソフトウェアデザインセンター セキュリティグループの耿です。
Amazon DynamoDB を利用する時、取得できる属性を特定の属性のみに制限したいことがあったため、IAM ポリシーを利用して実現する方法をまとめておきます。
ユースケースとしては、複数のアプリが同じ DynamoDB テーブルにアクセスするような構成において、特定のアプリには一部の属性しか見せたくないような場合です。
(あまり現実的ではないですが)簡単な例として、図のようにユーザー情報を保持する DynamoDB テーブルを「メール送信アプリ」と「データ分析アプリ」が利用しているとします。「メール送信アプリ」はユーザー名とメールアドレスだけを利用するのでそれ以外の属性は取得できないように制限をかけたいです。一方で「データ分析アプリ」は住所とジェンダーだけを利用するので、それ以外の属性は取得できないように制限をかけたいです。
これはアプリに許可を与える IAM ポリシーの Condition 句を利用することで実現できます。
DynamoDB テーブルの作成
まずは準備として図のように DynamoDB テーブルを作成します。パーティションキーとして user_id
を単独のプライマリーキーとし、サンプルデータを追加しておきます。
全属性の取得を許可する IAM ポリシーの付与
以下の IAM ポリシーでアプリケーションに GetItem
と Scan
の操作を許可します。取得できる属性を制限していないので、この状態では全属性を取得できるはずです。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:Scan" ], "Resource": "arn:aws:dynamodb:*:*:table/users" } ] }
データ取得の確認1
AWS SDK for JavaScript によるデータ取得のコードサンプルです。まずは特定のアイテムを取得する GetItem
操作です。
import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb"; const client = new DynamoDBClient({}); const command = new GetItemCommand({ TableName: "users", Key: { user_id: { S: "1ab24x" } }, }); const result = await client.send(command); console.log(result.Item);
アイテムの全属性を取得できました。
{ user_id: { S: '1ab24x' }, address: { S: '不思議の国X市' }, email: { S: 'alice@wonderland.com' }, user_name: { S: 'Alice' }, gender: { S: 'F' } }
次に、全アイテムを取得する Scan
操作です。
import { DynamoDBClient, ScanCommand } from "@aws-sdk/client-dynamodb"; const client = new DynamoDBClient({}); const command = new ScanCommand({ TableName: "users", }); const result = await client.send(command); console.log(result.Items);
こちらもアイテムの全属性を取得できました。
[ { user_id: { S: 'k4p1c3' }, address: { S: '鏡の国Y町' }, email: { S: 'bob@wonderland.com' }, user_name: { S: 'Bob' }, gender: { S: 'M' } }, { user_id: { S: '1ab24x' }, address: { S: '不思議の国X市' }, email: { S: 'alice@wonderland.com' }, user_name: { S: 'Alice' }, gender: { S: 'F' } } ]
(メイン)取得できる属性を制限する IAM ポリシーに修正
IAM ポリシーを修正し、明示的に許可した属性しか取得できないようにしていきます。以下のドキュメントを参考にします。
詳細に設定されたアクセスコントロールのための IAM ポリシー条件の使用
Condition 句に dynamodb:Attributes
と dynamodb:Select
を追加します。dynamodb:Attributes
には取得を許可したい属性名を配列で指定します。これにはプライマリーキーが含まれている必要があります。dynamodb:Select
には SPECIFIC_ATTRIBUTES
と記載します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:Scan" ], "Resource": "arn:aws:dynamodb:*:*:table/users", "Condition": { "ForAllValues:StringEquals": { "dynamodb:Attributes": [ "user_id", "user_name", "email" ] }, "StringEqualsIfExists": { "dynamodb:Select": "SPECIFIC_ATTRIBUTES" } } } ] }
データ取得の確認2
IAM ポリシーを変更した状態で前と同じ GetItem
操作を行うと、次のようなエラーになりました。
AccessDeniedException: User: arn:aws:iam::111122223333:user/<ユーザ名> is not authorized to perform: dynamodb:GetItem on resource: arn:aws:dynamodb:ap-northeast-1:111122223333:table/users because no identity-based policy allows the dynamodb:GetItem action
dynamodb:GetItem
が許可されていないというメッセージですが、実際には許可されていない属性を取得しようとしているためエラーが発生した状況です。次のようにプロジェクション式を利用して、許可された属性のみを取得するようにコマンドを変更します。
import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb"; const client = new DynamoDBClient({}); const command = new GetItemCommand({ TableName: "users", Key: { user_id: { S: "1ab24x" } }, ProjectionExpression: "user_id, user_name, email", // これを追加 }); const result = await client.send(command); console.log(result.Item);
こうするとエラーが解消し、結果も許可された属性のみが返却されていることがわかります。
{ user_id: { S: '1ab24x' }, email: { S: 'alice@wonderland.com' }, user_name: { S: 'Alice' } }
プロジェクション式に許可されていない属性(address
など)が含まれていると、前述と同じエラーが発生します。
Scan
操作についても、プロジェクション式を追加して取得する属性を明示的に指定することで、エラーなくスキャン操作ができます。
import { DynamoDBClient, ScanCommand } from "@aws-sdk/client-dynamodb"; const client = new DynamoDBClient({}); const command = new ScanCommand({ TableName: "users", ProjectionExpression: "user_id, user_name, email", // これを追加 }); const result = await client.send(command); console.log(result.Items);
許可された属性のみが返却されます。
[ { user_id: { S: 'k4p1c3' }, email: { S: 'bob@wonderland.com' }, user_name: { S: 'Bob' } }, { user_id: { S: '1ab24x' }, email: { S: 'alice@wonderland.com' }, user_name: { S: 'Alice' } } ]
まとめ
Condition 句に dynamodb:Attributes
と dynamodb:Select
を利用することで、特定の属性のみ取得できるような IAM ポリシーの作り方をまとめました。アイテムを取得する際にはプロジェクション式を利用する必要があるので、どの属性の取得が許可されているのかアプリ開発側でも知っておく必要があります。
執筆:@kou.kinyo、レビュー:@handa.kenta
(Shodoで執筆されました)