Amazon Web Services ブログ

AWS Key Management Service を使用した Amazon DynamoDB の暗号化と、Amazon Athena を使った API 呼び出しの分析

アプリケーションが進化してウェブに対してよりスケーラブルになるにつれて、お客様は柔軟なデータ構造とデータベースエンジンをそのユースケースに採用しています。現代のアプリケーションを構築するために NoSQL の柔軟なデータモデルを使用することから、NoSQL データストアを用いることがますます一般的になってきました。Amazon DynamoDB は高速で柔軟性のある NoSQL データベースサービスで、一貫して 1 桁ミリ秒のレイテンシーで大規模なサービスを提供しています。ウェブスケールのワークロードに DynamoDB を採用する際は、DynamoDB で利用できるセキュリティ管理を理解することが重要です。

DynamoDB を安全に実行するためにさまざまな機能を使用できます。Amazon VPC エンドポイントは、VPC で実行されているアプリケーションに DynamoDB テーブルへの安全なアクセスを提供します。また Amazon VPC エンドポイントは、AWS Identity and Access Management (IAM) を介してきめ細かいアクセス制御を提供し、DynamoDB テーブルに格納されたアイテムおよび属性へのアクセスを規制します。転送中のデータを暗号化するために Transport Layer Security (TLS) エンドポイントを使用することもできます。

保存データの暗号化には、テーブルを暗号化するために 2 つのカスタマーマスターキー (CMK) オプションのいずれかを選択できます。AWS が所有する CMK はデフォルトの暗号化タイプで、キーは CMK のコレクションとして AWS が所有し、複数の AWS アカウントでの使用を管理します。AWS が所有する CMK はお客様の AWS アカウントにはありません。一方、AWS が管理する CMK は、アカウントに保存されるキーで、AWS Key Management Service (AWS KMS) と統合された AWS のサービスでお客様に代わって作成、管理、および使用します。

AWS が所有する CMK を使用したサーバー側の休止中の暗号化は、すべての DynamoDB テーブルでデフォルトで有効になっています。DynamoDB は、AWS が所有する CMK を使用して以前は暗号化されなかったすべての既存のテーブルを暗号化します。ただし、AWS が管理する CMK を使用して、テーブルの一部または全部を暗号化するオプションも選択できます。さらに、データを DynamoDB に送信する前にクライアント側の暗号化を使用してデータを保護することができます。

このブログ記事では、AWS が管理する CMK を使用したサーバー側の暗号化の仕組みについて説明します。また、AWS CloudTrailAmazon Athena を使用して AWS KMS への API 呼び出しを追跡する方法についても説明します (GenerateGrant とDecrypt)。

DynamoDB テーブルを作成する

AWS が管理する CMK を使用して DynamoDB テーブルを作成することから始めましょう。AWS KMS の属性 -sse-specification EnabledSSEType として暗号化の方法を定義します。この場合、AWS が管理する CMK です。

aws dynamodb create-table \
    --table-name ratings \
    --attribute-definitions AttributeName=player,AttributeType=S \
 AttributeName=rating,AttributeType=N \
    --key-schema AttributeName=player,KeyType=HASH  AttributeName=rating,KeyType=RANGE \
    --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
    --sse-specification Enabled=true,SSEType=KMS

AWS CLI コマンドの次の応答出力を確認すると、SSEDescription StatusENABLED に設定されます。 SSETypeKMS で、サーバー側の暗号化に使用する KMS キーの Amazon リソースネーム (ARN) があります。

応答出力

{
    "TableDescription": {
        "TableArn": "arn:aws:dynamodb:us-east-1:904672585901:table/ratings",
        "AttributeDefinitions": [
            {
                "AttributeName": "player",
                "AttributeType": "S"
            },
            {
                "AttributeName": "rating",
                "AttributeType": "N"
            }
        ],
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "WriteCapacityUnits": 5,
            "ReadCapacityUnits": 5
        },
        "TableSizeBytes": 0,
        "TableName": "ratings",
        "TableStatus": "CREATING",
        "TableId": "e6befed6-86c8-4b4d-b800-9be1062eb67b",
        "SSEDescription": {
            "Status": "ENABLED",
            "KMSMasterKeyArn": "arn:aws:kms:us-east-1:904672585901:key/af886ef7-08d3-4c1b-bd14-70d2b722e165",
            "SSEType": "KMS"
        },
        "KeySchema": [
            {
                "KeyType": "HASH",
                "AttributeName": "player"
            },
            {
                "KeyType": "RANGE",
                "AttributeName": "rating"
            }
        ],
        "ItemCount": 0,
        "CreationDateTime": 1552353132.722
    }
}

注:サーバー側で暗号化されたテーブルのレスポンスに SSEDescription が表示されない場合は、最新の AWS CLI に更新してみてください。

テーブルの暗号化を確認する

テーブルの暗号化方法を確認したい場合は、describe-table API 呼び出しまたは DynamoDB コンソールを使用できます。

aws dynamodb describe-table –-table-name ratings \
     --query 'Table.{TableName:TableName, TableStatus:TableStatus, SSEDescription:SSEDescription}' --output json

以下のように、--query パラメータを使用して、応答出力内に必要な属性のみをフィルタリングして印刷することができます。次の応答出力から、テーブルが ACTIVE で、SSEDescription オブジェクトのステータス属性が ENABLED で、AWS KMS が SSEType であることがわかります。

応答出力

{
    "TableStatus": "ACTIVE",
    "TableName": "ratings",
    "SSEDescription": {
        "Status": "ENABLED",
        "KMSMasterKeyArn": "arn:aws:kms:us-east-1:904672585901:key/af886ef7-08d3-4c1b-bd14-70d2b722e165",
        "SSEType": "KMS"
    }
}

サーバー側の暗号化のしくみ

これで、評価テーブルが AWS KMS のサーバー側の暗号化で作成されたことがわかったので、サーバー側の暗号化のワークフローを見てみましょう。

サーバー側の暗号化のワークフロー

前の図に示したように、サーバー側の暗号化プロセスの手順は次のとおりです。

  1. テーブルの所有者は、サーバー側の暗号化を AWS KMS に設定して CreateTable API 呼び出しを使用します。
  2. CreateTable API リクエストを受信すると、DynamoDB はそのリクエストを認証します。
  3. DynamoDB は AWS が管理する CMK を最上位キーとして使用します。DynamoDB はサーバー側の暗号化にこのキーを使用する必要があるため、最初の手順は CreateGrant API 呼び出しのセットを作成することです。
  4. DynamoDB は CMK を使用してテーブルキーを生成します。テーブルキーは各テーブルに固有のキーです。このテーブルキーは、テーブル内の基本構造を暗号化するために使用するデータ暗号化キーを生成するために使用します。
  5. プレーンテキストのキーマテリアルと暗号化されたキーマテリアルは DynamoDB に送信されます。
  6. プレーンテキストテーブルキーは DynamoDB にキャッシュされます。

次の図は、DynamoDB が使用するサーバー側の暗号化キーの階層を示しています。DynamoDB は、各テーブルの一意のテーブルキーを生成して暗号化するための最上位キーとして、AWS アカウントの各 AWS リージョンの AWS KMS が管理する CMK を使用します。DynamoDB はテーブルキーを使用してデータ暗号化キーを生成し、次にデータ暗号化キーを使用してテーブルデータとテーブル内の基礎となる構造を暗号化します。

DynamoDB が使用するサーバー側の暗号化キーの階層

テーブルを作成したので、PutItem API 呼び出しを使用しながらその仕組みを見てみましょう。

PutItem API 呼び出しを使用している間の仕組み

PutItem API 呼び出しを使用する場合:

  1. ユーザーが PutItem 呼び出しを発行して、データを DynamoDB テーブルに追加します。
  2. DynamoDB はユーザーのリクエストを認証します。
  3. DynamoDB は、ユーザーが DynamoDB テーブルにデータを書き込むために必要な権限を持っていることを確認します。
  4. 暗号化されているデータに応じて、DynamoDB はデータを暗号化するための正しいデータ暗号化キーを識別します。DynamoDB がすべての DynamoDB 操作に対して KMS を呼び出さないようにするため、テーブルキーは各プリンシパルごとにメモリにキャッシュされます。テーブルキーは、アクティブトラフィックとのクライアント接続ごとに 5 分に 1 回更新されます。DynamoDB が 5 分間の非アクティブ状態の後にキャッシュされたテーブルキーのリクエストを取得すると、テーブルを復号化するために新しいリクエストを KMS に送信します。
  5. 暗号化されたデータと暗号化されたキーマテリアルは DynamoDB に保存されます。

DynamoDB テーブルにデータを挿入したので、GetItem API 呼び出しでデータを取得する仕組みを見てみましょう。

GetItem API 呼び出しでデータを取得する仕組み

GetItem API 呼び出しを使用している場合:

  1. ユーザーが GetItem 呼び出しを発行して DynamoDB テーブルからデータを取得します。
  2. DynamoDB はユーザーのリクエストを認証します。
  3. DynamoDB は、ユーザーが DynamoDB テーブルからデータを読み取るために必要な権限を持っていることを確認します。
  4. データを取得するリクエストが出されます。
  5. 暗号化されたデータを取得します。
  6. DynamoDB は、各プリンシパルのプレーンテキストテーブルキーをメモリにキャッシュします。DynamoDB が 5 分間の非アクティブ状態の後にキャッシュされたテーブルキーのリクエストを取得すると、テーブルキーを復号化するために新しいリクエストを AWS KMS に送信します。
  7. 復号化されたプレーンテキストのキーマテリアルを取得します。
  8. 受信したプレーンテキストのキーマテリアルを使用してデータを復号します。
  9. HTTPS を使用してプレーンテキストデータをユーザーに送信します (TLS エンドポイントのみ)。

注: 次のセクションには CloudTrail ログが必要です。アカウントで CloudTrail が有効になっていることを確認してください。詳しくは、「CloudTrail 入門」を参照してください。

CloudTrail ログと Athena を使用して KMS のキー使用状況を分析する

CloudTrail は API 呼び出しを記録し、ログファイルを Amazon S3 に公開します。アカウントのアクティビティは CloudTrail ログファイルでイベントとして追跡します。各イベントには、アクションを実行したユーザー、アクションの日時、影響を受けたリソースなどの情報が含まれています。複数のイベントがまとめられて CloudTrail ログファイルに JSON 形式で構造化されます。DynamoDB が CMK で許可を作成するために API 呼び出しを行うと、それらは CloudTrail によって記録されます。さらに、DynamoDB がテーブルキーを生成するための API 呼び出しまたは復号化するための API 呼び出しを行うと、それらは CloudTrail によって記録されます。この記事では、Amazon S3 に保存された CloudTrail ログを分析して AWS KMS と DynamoDB への呼び出しを理解するために、インタラクティブな SQL クエリサービスである Athena を使用します。

次のサンプルクエリは、日付範囲、AWS KMS への呼び出し数、および API 呼び出しタイプ別の分配について DynamoDB テーブルに対して行われた呼び出しをリストしています。ただし、クエリを実行する前に、CloudTrail ログの構造を記述する外部テーブルを Athena に作成する必要があります。

Athena にテーブルを作成する

テーブルを作成するには、Athena コンソールで次の CREATE EXTERNAL TABLE コマンドを使用します。Amazon S3 バケットの名前と場所を、自身の Amazon S3 バケットの名前と場所に置き換えます。

CREATE EXTERNAL TABLE cloudtrail_logs_<s3 bucket name> (
eventversion STRING,
userIdentity STRUCT<
  type:STRING,
  principalid:STRING,
  arn:STRING,
  accountid:STRING,
  invokedby:STRING,
  accesskeyid:STRING,
  userName:STRING,
  sessioncontext:STRUCT<
    attributes:STRUCT<
      mfaauthenticated:STRING,
      creationdate:STRING>,
    sessionIssuer:STRUCT<
      type:STRING,
      principalId:STRING,
      arn:STRING,
      accountId:STRING,
      userName:STRING>>>,
eventTime STRING,
eventSource STRING,
eventName STRING,
awsRegion STRING,
sourceIpAddress STRING,
userAgent STRING,
errorCode STRING,
errorMessage STRING,
requestParameters STRING,
responseElements STRING,
additionalEventData STRING,
requestId STRING,
eventId STRING,
resources ARRAY<STRUCT<
  ARN:STRING,accountId:
  STRING,type:STRING>>,
eventType STRING,
apiVersion STRING,
readOnly STRING,
recipientAccountId STRING,
serviceEventDetails STRING,
sharedEventID STRING,
vpcEndpointId STRING
)
PARTITIONED BY(year string, month string, day string)
ROW FORMAT SERDE 'com.amazon.emr.hive.serde.CloudTrailSerde'
STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://<Your CloudTrail s3 bucket>/AWSLogs/<AWS_Account_ID>/CloudTrail/<region>’;

上記のコード例に示すように、CloudTrail ログは次の形式で Amazon S3 に配信されます: s3://&lt;your-cloudtrail-s3-bucket&gt;/AWSLogs/&lt;AWS account-number&gt;/CloudTrail/region/year/month/date。作成した Athena 外部テーブルは、上記のコード例の PARTITIONED BY 構文で指定されているように年、月、日でパーティション化されています。

次のステップは、以下のコマンドでテーブルにパーティションを追加することです。Athena コンソールで直接コマンドを実行できます。ここで、year='2019'month=’03’day=’11’ のパーティションを追加します。データをパーティション化すると、各クエリでスキャンされるデータ量が制限されるため、パフォーマンスが向上し、コストが削減されます。Athena でのデータのパーティション化の詳細については、「Athena ユーザーガイド」の「データのパーティション化」を参照してください。

ALTER TABLE cloudtrail_logs_<s3 bucket name>
ADD PARTITION (year=’2019’, month=’03’, day= ‘11’)
LOCATION ‘s3://<your-cloudtrail-s3-bucket>/AWSLogs/<account-number>/CloudTrail/<region>/2019/03/11/’;

Athena で外部パーティションテーブルを作成し、パーティションデータを追加したので、いくつかクエリを実行しましょう。

クエリ例

次のクエリ例では、前の例の ALTER コマンドのとおり、year='2019'month='03'AND day='11'のパーティションデータを使用します。Athena のパーティションデータに基づいてこれらの値を変更してください。

クエリ例 1: このクエリは、CloudTrail ログテーブルから指定された日に DynamoDB に対して行われた API 呼び出しを返します。これは、eventsource = ‘dynamodb.amazonaws.com’ でフィルタリングすることで実行します。返されるレコード数を 1,000 に制限します。

SELECT * FROM cloudtrail_logs_<S3 bucket name> 
WHERE year='2019' AND month='03' AND day='11'
AND eventsource = 'dynamodb.amazonaws.com'
LIMIT 1000;

クエリ例 2: 前のクエリでは、eventsource = “dynamodb.amazonaws.com” を持つ使用可能なすべての属性を取得しました。それでは、dynamodb.amazonaws.com からイベントソース kms.amazonaws.com に対して行われた API 呼び出しでセレクト列と属性を指定して、出力をさらにフィルタリングしましょう。出力には、DynamoDB から KMS に対して行われた呼び出しが表示されるはずです。

SELECT eventtime, eventname, sourceipaddress, useridentity, requestparameters FROM cloudtrail_logs_<S3 bucket name> 
WHERE year='2019' AND month='03' AND day='11'
AND eventsource = 'kms.amazonaws.com'
AND sourceipaddress = 'dynamodb.amazonaws.com'
LIMIT 1000;

クエリ例 3: KMS に対して行われた API 呼び出しを確認しましょう。次のクエリは、特定のテーブルに対して行われた一連の API 呼び出しを識別するのに役立ちます。your-table-name を、クエリを行いたい DynamoDB テーブルの名前に置き換えます。AWS KMS に対して行われた API 呼び出しのタイムラインを理解するために、結果を eventtime で並べることができます。Athena の出力に decrypt eventnames が表示されます。

SELECT eventtime, eventname, sourceipaddress, useridentity, requestparameters FROM cloudtrail_logs_<S3 bucket name> 
WHERE year='2019' AND month='03' AND day='11'
AND eventsource = 'kms.amazonaws.com'
AND sourceipaddress = 'dynamodb.amazonaws.com'
AND REPLACE(JSON_EXTRACT_SCALAR(requestparameters, '$.encryptionContext.aws:dynamodb:tableName'),'"','') = 'your-table-name'
ORDER BY eventtime
LIMIT 1000;

クエリ例 4: すべての AWS リージョンには、テーブルキーの生成に使用する一意の KMS CMK があります。このクエリは、サーバー側の休止中の暗号化に特定のキーを使用しているテーブルを特定するのに役立ちます。arn:aws:kms:your-region:your-account-number:key/your-key-id を、関心のある AWS リージョンの KMS CMK の ARN に置き換えます。Athena の出力には、eventname と KMS キーを使用している DynamoDB テーブルが表示されるはずです。

SELECT eventtime, eventname, REPLACE(JSON_EXTRACT_SCALAR(requestparameters, '$.encryptionContext.aws:dynamodb:tableName'),'"','') as ddbtbl FROM cloudtrail_logs_<S3 bucket name> 
WHERE year='2019' and month='03' and day='11'
AND eventsource = 'kms.amazonaws.com'
AND sourceipaddress = 'dynamodb.amazonaws.com'
AND resources[1].arn = 'arn:aws:kms:your-region:your-account-number:key/your-key-id' 
ORDER BY ddbtbl
LIMIT 1000;

まとめ

このブログ記事では、DynamoDB を使用した暗号化オプションの概要と、AWS が管理する CMK を使用したサーバー側の暗号化を用いて DynamoDB テーブルを作成するプロセスについて説明しました。テーブルを作成したり、テーブルへアイテムを追加したり、暗号化が有効になっている DynamoDB テーブルからアイテムを取得したりする際の、DynamoDB API のワークフローと KMS の相互作用を確認しました。DynamoDB で使用する暗号化キーの階層も調べました。次に、Athena を使用して、関連情報を取得するために CloudTrail ログを分析しました。この情報には、DynamoDB テーブルでの KMS API 呼び出しアクティビティ、API 呼び出しの数と種類、および DynamoDB テーブルへのサービスキーのマッピングが含まれます。まとめると、これにより、DynamoDB の暗号化および AWS KMS との相互作用についてさらに詳しく知ることができます。


著者について

Sai Sriparasa は AWS プロフェッショナルサービスのシニアビッグデータ & セキュリティコンサルタントです。彼はお客様と協力して AWS の自動化、運用、ガバナンス、セキュリティに重点を置いた戦略的かつ戦術的なビッグデータソリューションを提供しています。余暇はスポーツや時事のニュースチェックをしています。

 

 

 

 

Prahlad Rao は、AWS のソリューションアーキテクトで、データベースとビッグデータを専門としています。彼は企業の顧客と協力してクラウドへの旅をナビゲートし、クラウド向けのアプリケーションを最適化する手助けをしています。