CloudTrail Lake で保存されたクエリ結果を検証する - AWS CloudTrail

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

CloudTrail Lake で保存されたクエリ結果を検証する

がクエリ結果を CloudTrail 配信した後にクエリ結果が変更、削除、または変更されていないかどうかを判断するには、 CloudTrail クエリ結果の整合性検証を使用できます。この機能は、業界標準のアルゴリズムを使用して構築されています。ハッシュ用の SHA-256 とデジタル署名用の RSA を備えた SHA-256。これにより、 CloudTrail クエリ結果ファイルを検出せずに変更、削除、または偽造することが計算上実行不可能になります。コマンドラインを使用してクエリ結果ファイルを検証できます。

使用する理由

検証されたクエリ結果ファイルは、セキュリティおよびフォレンシック調査で非常に重要です。たとえば、検証済みのクエリ結果ファイルを使用することで、クエリ結果ファイル自体が変更されていないことを明確に主張できます。 CloudTrail クエリ結果ファイルの整合性検証プロセスでは、クエリ結果ファイルが削除されたか変更されたかもわかります。

で保存されたクエリ結果を検証する AWS CLI

aws cloudtrail verify-query-results コマンドを使用して、クエリ結果ファイルの整合性を検証してファイルに署名できます。

前提条件

コマンドラインを使用してクエリ結果の整合性を検証するには、次の条件を満たしている必要があります。

  • へのオンライン接続が必要です AWS。

  • AWS CLI バージョン 2 を使用する必要があります。

  • ローカルでクエリ結果ファイルを検証してファイルに署名する場合、次の条件が適用されます。

    • 指定したファイルパスにクエリ結果ファイルと署名ファイルを配置する必要があります。--local-export-path パラメータの値としてファイルパスを指定します。

    • クエリ結果ファイルと署名ファイルの名前は変更しません。

  • S3 バケットでクエリ結果ファイルを検証してファイルに署名する場合、次の条件が適用されます。

    • クエリ結果ファイルと署名ファイルの名前は変更しません。

    • クエリ結果ファイルの署名ファイルを含む Amazon S3 バケットへの読み取りアクセスが必要です。

    • 指定した S3 プレフィックスには、クエリ結果ファイルと署名ファイルが含まれている必要があります。--s3-prefix パラメータの値として S3 プレフィックスを指定します。

verify-query-results

verify-query-results コマンドは、各クエリ結果ファイルのハッシュ値を署名ファイル内の fileHashValue と比較し、署名ファイルの hashSignature を検証することによってクエリ結果ファイルのハッシュ値を検証します。

クエリ結果を検証する場合、--s3-bucket および --s3-prefix のいずれかのコマンドラインオプションを使用して S3 バケットに保存されているクエリ結果ファイルと署名ファイルを検証するか、--local-export-path コマンドラインオプションを使用して、ダウンロードしたクエリ結果ファイルと署名ファイルのローカル検証を実行することができます。

注記

verify-query-results コマンドはリージョン固有です。特定の のクエリ結果を検証するには、--regionグローバルオプションを指定する必要があります AWS リージョン。

verify-query-results コマンドのオプションを以下に示します。

--s3-bucket <文字列>

クエリ結果ファイルと署名ファイルを保存する S3 バケット名を指定します。このパラメータは --local-export-path と共に使用できません。

--s3-prefix <文字列>

クエリ結果ファイルと署名ファイルを含む S3 フォルダーの S3 パスを指定します (s3/path/ など)。このパラメータは --local-export-path と共に使用できません。ファイルが S3 バケットのルートディレクトリにある場合は、このパラメータを指定する必要はありません。

--local-export-path <文字列>

クエリ結果ファイルと署名ファイルを含むローカルディレクトリを指定します (/local/path/to/export/file/ など)。このパラメータは --s3-bucket や --s3-prefix と共に使用できません。

次の例では、--s3-bucket および --s3-prefix コマンドラインオプションを使用してクエリ結果を検証し、クエリ結果ファイルと署名ファイルを含む S3 バケット名とプレフィックスを指定します。

aws cloudtrail verify-query-results --s3-bucket DOC-EXAMPLE-BUCKET --s3-prefix prefix --region region

次の例では、--local-export-path コマンドラインオプションを使用して、ダウンロードしたクエリ結果を検証し、クエリ結果ファイルと署名ファイルのローカルパスを指定します。クエリ結果ファイルのダウンロードの詳細については、「 CloudTrail Lake に保存されたクエリ結果をダウンロードする」を参照してください。

aws cloudtrail verify-query-results --local-export-path local_file_path --region region

検証結果

次の表は、クエリ結果ファイルと書名ファイルの検証メッセージを示しています。

ファイルタイプ 検証メッセージ 説明
Sign file Successfully validated sign and query result files 書名ファイルの署名は有効です。参照しているクエリ結果ファイルを確認できます。
Query result file

ValidationError: "File file_name has inconsistent hash value with hash value recorded in sign file, hash value in sign file is expected_hash, but get computed_hash

クエリ結果ファイルのハッシュ値が署名ファイルの fileHashValue と一致しなかったため、検証に失敗しました。
Sign file

ValidationError: Invalid signature in sign file

署名が無効なため、署名ファイルの検証に失敗しました。

CloudTrail 署名ファイル構造

署名ファイルには、クエリ結果を保存したときに Amazon S3 バケットに送られた各クエリ結果ファイルの名前、各クエリ結果ファイルのハッシュ値、ファイルのデジタル署名が含まれます。デジタル署名とハッシュ値は、クエリ結果ファイルおよび署名ファイル自体の整合性を検証するために使用されます。

署名ファイルの場所

署名ファイルは、次の構文で表される Amazon S3 バケットの場所に送られます。

s3://DOC-EXAMPLE-BUCKET/optional-prefix/AWSLogs/aws-account-ID/CloudTrail-Lake/Query/year/month/date/query-ID/result_sign.json

署名ファイルのコンテンツの例

次の署名ファイルの例には、 CloudTrail Lake クエリ結果に関する情報が含まれています。

{ "version": "1.0", "region": "us-east-1", "files": [ { "fileHashValue" : "de85a48b8a363033c891abd723181243620a3af3b6505f0a44db77e147e9c188", "fileName" : "result_1.csv.gz" } ], "hashAlgorithm" : "SHA-256", "signatureAlgorithm" : "SHA256withRSA", "queryCompleteTime": "2022-05-10T22:06:30Z", "hashSignature" : "7664652aaf1d5a17a12ba50abe6aca77c0ec76264bdf7dce71ac6d1c7781117c2a412e5820bccf473b1361306dff648feae20083ad3a27c6118172a81635829bdc7f7b795ebfabeb5259423b2fb2daa7d1d02f55791efa403dac553171e7ce5f9307d13e92eeec505da41685b4102c71ec5f1089168dacde702c8d39fed2f25e9216be5c49769b9db51037cb70a84b5712e1dffb005a74580c7fdcbb89a16b9b7674e327de4f5414701a772773a4c98eb008cca34228e294169901c735221e34cc643ead34628aabf1ba2c32e0cdf28ef403e8fe3772499ac61e21b70802dfddded9bea0ddfc3a021bf2a0b209f312ccee5a43f2b06aa35cac34638f7611e5d7", "publicKeyFingerprint" : "67b9fa73676d86966b449dd677850753" }

署名ファイルのフィールドの説明

以下では、署名ファイルの各フィールドについて説明します。

version

署名ファイルのバージョン。

region

クエリ結果の保存に使用される AWS アカウントのリージョン。

files.fileHashValue

圧縮されたクエリ結果ファイルの内容の 16 進エンコードされたハッシュ値です。

files.fileName

クエリ結果ファイルの名前。

hashAlgorithm

クエリ結果ファイルのハッシュ計算に使用されたハッシュアルゴリズムです。

signatureAlgorithm

ファイルの署名に使用されるアルゴリズムです。

queryCompleteTime

がクエリ結果を S3 バケットに CloudTrail いつ配信したかを示します。この値を使用してパブリックキーを検索できます。

hashSignature

ファイルのハッシュ署名。

publicKeyFingerprint

このファイルの署名に使用されたパブリックキーの 16 進エンコードされたフィンガープリントです。

CloudTrail クエリ結果ファイルの整合性検証のカスタム実装

CloudTrail は業界標準の公開されている暗号化アルゴリズムとハッシュ関数を使用するため、独自のツールを作成して CloudTrail クエリ結果ファイルの整合性を検証できます。クエリ結果を Amazon S3 バケットに保存すると、 は署名ファイルを S3 バケットに CloudTrail 配信します。独自の検証ソリューションを実装して、署名ファイルとクエリ結果ファイルを検証できます。署名ファイルの詳細については、「CloudTrail 署名ファイル構造」を参照してください。

このトピックでは、署名ファイルの署名方法について説明し、署名ファイルと、署名ファイルによって参照される署名ファイルを検証するソリューションの実装に必要な手順を詳しく示します。

CloudTrail 署名ファイルの署名方法を理解する

CloudTrail 署名ファイルは RSA デジタル署名で署名されます。署名ファイルごとに、 CloudTrail は以下を実行します。

  1. 各クエリ結果ファイルのハッシュ値を含むハッシュリストを作成します。

  2. リージョンに固有のプライベートキーを取得します。

  3. 文字列の SHA-256 ハッシュとプライベートキーを RSA 署名アルゴリズムに渡すと、そこでデジタル署名が作成されます。

  4. 署名のバイトコードを 16 進形式にエンコードします。

  5. デジタル署名を署名ファイルに入力します。

データ署名文字列の内容

データ署名文字列は、スペースで区切られた各クエリ結果ファイルのハッシュ値で構成されます。署名ファイルには、各クエリ結果ファイルの fileHashValue がリストされています。

カスタム検証を実装する手順

カスタム検証ソリューションを実装するときは、最初にダイジェストファイルを検証してから、署名ファイルと参照するクエリ結果ファイルを検証する必要があります。

署名ファイルを検証する

署名ファイルを検証するには、署名、対応するプライベートキーが署名に使用されたパブリックキー、計算したデータ署名文字列が必要です。

  1. 署名ファイルを入手してください。

  2. 本来の場所から署名ファイルが取得されたことを確認します。

  3. 署名ファイルの 16 進エンコードされた署名を取得します。

  4. パブリックキー (対応するプライベートキーが署名ファイルの署名に使用された) の 16 進エンコードされたフィンガープリントを取得します。

  5. 署名ファイルで queryCompleteTime に対応する時間範囲のパブリックキーを取得します。時間範囲には、「StartTime より早い queryCompleteTime」および「EndTime より遅い queryCompleteTime」を選択します。

  6. 取得したパブリックキーの中から、フィンガープリントが署名ファイルの publicKeyFingerprint の値と一致するパブリックキーを選択します。

  7. 各クエリ結果ファイルのハッシュ値をスペースで区切ったハッシュリストを使用して、署名ファイルの署名を検証するために使用するデータ署名文字列を再作成します。署名ファイルには、各クエリ結果ファイルの fileHashValue がリストされています。

    たとえば、署名ファイルの files 配列に次の 3 つのクエリ結果ファイルが含まれている場合、ハッシュリストは「aaa bbb ccc」になります。

    “files": [
 {
 "fileHashValue" : “aaa”,
 "fileName" : "result_1.csv.gz"
 }, {
 "fileHashValue" : “bbb”,
 "fileName" : "result_2.csv.gz"
 }, {
 "fileHashValue" : “ccc”,
 "fileName" : "result_3.csv.gz"
 } ],
  8. 文字列の SHA-256 ハッシュ、パブリックキー、署名を、パラメータとしてRSA 署名検証アルゴリズムに渡して、署名を検証します。結果が true の場合、署名ファイルは有効です。

クエリ結果ファイルを検証する

署名ファイルが有効な場合は、署名ファイルが参照するクエリ結果ファイルを検証します。クエリ結果ファイルの整合性を検証するには、圧縮されたコンテンツの SHA-256 ハッシュ値を計算し、その結果を署名ファイルに記録されているクエリ結果ファイルの fileHashValue と比較します。ハッシュが一致する場合、クエリ結果ファイルは有効です。

以下のセクションではこの検証を詳しく説明します。

A. 署名ファイルを取得する

最初の手順は、署名ファイルを取得し、パブリックキーのフィンガープリントを取得することです。

  1. 検証するクエリ結果の署名ファイルを Amazon S3 バケットから取得します。

  2. 次に、署名ファイルから hashSignature の値を取得します。

  3. 署名ファイルで、署名ファイルの署名に使用されたプライベートキーに対応するパブリックキーのフィンガープリントを publicKeyFingerprint フィールドから取得します。

B. 署名ファイルの検証のためにパブリックキーを取得する

署名ファイルを検証するためのパブリックキーを取得するには、 AWS CLI または CloudTrail API を使用できます。どちらの場合も、検証しようとする署名ファイルの時間範囲 (開始時刻と終了時刻) を指定します。署名ファイル内の queryCompleteTime に対応する時間範囲を使用してください。指定した時間範囲について 1 つ以上のパブリックキーが返されることがあります。返されたキーの有効な時間範囲が重複する可能性があります。

注記

CloudTrail はリージョンごとに異なるプライベート/パブリックキーペアを使用するため、各署名ファイルはリージョンに固有のプライベートキーで署名されます。したがって、特定のリージョンの署名ファイルを検証するときは、同じリージョンからパブリックキーを取得する必要があります。

AWS CLI を使用してパブリックキーを取得する

を使用して署名ファイルのパブリックキーを取得するには AWS CLI、 cloudtrail list-public-keys コマンドを使用します。このコマンドの形式は次のとおりです。

aws cloudtrail list-public-keys [--start-time <start-time>] [--end-time <end-time>]

start-time および end-time パラメータには UTC タイムスタンプを使用します。これらはオプションです。指定しない場合、現在の時刻が使用され、現在アクティブなパブリックキー (1 つまたは複数) が返されます。

レスポンス例

レスポンスは、返されるキー (1 つまたは複数) を表す JSON オブジェクトのリストです。

CloudTrail API を使用してパブリックキーを取得する

CloudTrail API を使用して署名ファイルのパブリックキーを取得するには、開始時刻と終了時刻の値を ListPublicKeys API に渡します。この ListPublicKeys API は、指定された時間範囲内の、対応するプライベートキーが署名ファイルの署名に使用されたパブリックキーを返します。API は、各パブリックキーに対応するフィンガープリントも返します。

ListPublicKeys

このセクションでは、ListPublicKeys API のリクエストパラメータとレスポンス要素について説明します。

注記

ListPublicKeys のバイナリフィールドのエンコードは変更される可能性があります。

リクエストパラメータ

名前 説明
StartTime

オプションで、 CloudTrail 署名ファイルのパブリックキーを検索する時間範囲の開始を UTC で指定します。が指定され StartTime ていない場合、現在の時刻が使用され、現在のパブリックキーが返されます。

タイプ: DateTime

EndTime

オプションで、 CloudTrail 署名ファイルのパブリックキーを検索する時間範囲の終了を UTC で指定します。が指定されていない場合 EndTime は、現在の時刻が使用されます。

タイプ: DateTime

レスポンス要素

PublicKeyList は、次の要素を含む PublicKey オブジェクトの配列です。

名前 説明
Value

DER エンコードされたパブリックキー値 (PKCS #1 形式)。

型: Blob

ValidityStartTime

パブリックキーの有効期間の開始時刻。

タイプ: DateTime

ValidityEndTime

パブリックキーの有効期間の終了時刻。

タイプ: DateTime

Fingerprint

パブリックキーのフィンガープリント。フィンガープリントを使用して、署名ファイルの検証に使用する必要があるパブリックキーを特定できます。

型: 文字列

C. 検証に使用するパブリックキーを選択する

list-public-keys または ListPublicKeys によって取得されたパブリックキーの中から、そのフィンガープリントが署名ファイルの publicKeyFingerprint フィールドに記録されているフィンガープリントと一致するパブリックキーを選択します。これは署名ファイルの検証に使用するパブリックキーです。

D. データ署名文字列を再作成する

署名ファイルの署名と、関連付けられたパブリックキーを取得しました。次は、データ署名文字列を計算する必要があります。データ署名文字列の計算が完了すると、署名の検証に必要な入力を得られます。

データ署名文字列は、スペースで区切られた各クエリ結果ファイルのハッシュ値で構成されます。この文字列を再作成した後、署名ファイルを検証できます。

E. 署名ファイルを検証する

再作成したデータ署名文字列、デジタル署名、パブリックキーを、RSA 署名検証アルゴリズムに渡します。出力が true の場合、署名ファイルの署名が検証され、署名ファイルは有効です。

F. クエリ結果ファイルを検証する

署名ファイルの検証が完了したら、クエリ結果ファイルが参照するログファイルを検証することができます。署名ファイルにはクエリ結果ファイルの SHA-256 ハッシュが含まれています。クエリ結果ファイルの 1 つが CloudTrail 配信後に変更された場合、SHA-256 ハッシュは変更され、署名ファイルの署名は一致しません。

以下の手順を使用して、署名ファイルの files 配列にリストされているクエリ結果ファイルを検証します。

  1. 署名ファイル内で、files.fileHashValue フィールドからファイルの元のハッシュを取得します。

  2. hashAlgorithm で指定されたハッシュアルゴリズムを使用して、クエリ結果ファイルの圧縮されたコンテンツをハッシュします。

  3. クエリ結果ファイルごとに生成したハッシュ値を署名ファイルの files.fileHashValue と比較します。ハッシュが一致する場合、クエリ結果ファイルは有効です。

署名とクエリ結果ファイルのオフライン検証

署名ファイルとクエリ結果ファイルをオフラインで検証するとき、通常は前のセクションで説明した手順に従います。ただし、パブリックキーに関する次の情報を考慮する必要があります。

パブリックキー

オフラインで検証するには、所定の時間範囲のクエリ結果ファイルの検証に必要なパブリックキーを最初にオンラインで取得し (たとえば、ListPublicKeys を呼び出す)、オフラインで保存する必要があります。指定した最初の時間範囲外の他のファイルを検証するには、常にこの手順を繰り返す必要があります。

検証のサンプルスニペット

次のサンプルスニペットは、 CloudTrail 署名ファイルとクエリ結果ファイルを検証するためのスケルトンコードを提供します。このスケルトンコードはオンラインでもオフラインでも使用できます。つまり、実装する際に AWSとのオンライン接続を使用するかどうかはユーザーが決めることができます。推奨の実装では、Java Cryptography Extension (JCE)Bouncy Castle をセキュリティ プロバイダーとして使用しています。

サンプルスニペットには次の内容が含まれます。

  • 署名ファイルの署名の検証に使用されるデータ署名文字列を作成する方法。

  • 署名ファイルの署名を確認する方法。

  • クエリ結果ファイルのハッシュ値を計算し、それを署名ファイルにリストされている fileHashValue と比較して、クエリ結果ファイルの信頼性を検証する方法。

import org.apache.commons.codec.binary.Hex; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.RSAPublicKey; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.json.JSONArray; import org.json.JSONObject; import java.security.KeyFactory; import java.security.MessageDigest; import java.security.PublicKey; import java.security.Security; import java.security.Signature; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class SignFileValidationSampleCode { public void validateSignFile(String s3Bucket, String s3PrefixPath) throws Exception { MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); // Load the sign file from S3 (using Amazon S3 Client) or from your local copy JSONObject signFile = loadSignFileToMemory(s3Bucket, String.format("%s/%s", s3PrefixPath, "result_sign.json")); // Using the Bouncy Castle provider as a JCE security provider - http://www.bouncycastle.org/ Security.addProvider(new BouncyCastleProvider()); List<String> hashList = new ArrayList<>(); JSONArray jsonArray = signFile.getJSONArray("files"); for (int i = 0; i < jsonArray.length(); i++) { JSONObject file = jsonArray.getJSONObject(i); String fileS3ObjectKey = String.format("%s/%s", s3PrefixPath, file.getString("fileName")); // Load the export file from S3 (using Amazon S3 Client) or from your local copy byte[] exportFileContent = loadCompressedExportFileInMemory(s3Bucket, fileS3ObjectKey); messageDigest.update(exportFileContent); byte[] exportFileHash = messageDigest.digest(); messageDigest.reset(); byte[] expectedHash = Hex.decodeHex(file.getString("fileHashValue")); boolean signaturesMatch = Arrays.equals(expectedHash, exportFileHash); if (!signaturesMatch) { System.err.println(String.format("Export file: %s/%s hash doesn't match.\tExpected: %s Actual: %s", s3Bucket, fileS3ObjectKey, Hex.encodeHexString(expectedHash), Hex.encodeHexString(exportFileHash))); } else { System.out.println(String.format("Export file: %s/%s hash match", s3Bucket, fileS3ObjectKey)); } hashList.add(file.getString("fileHashValue")); } String hashListString = hashList.stream().collect(Collectors.joining(" ")); /* NOTE: To find the right public key to verify the signature, call CloudTrail ListPublicKey API to get a list of public keys, then match by the publicKeyFingerprint in the sign file. Also, the public key bytes returned from ListPublicKey API are DER encoded in PKCS#1 format: PublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, PublicKey BIT STRING } AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } */ byte[] pkcs1PublicKeyBytes = getPublicKey(signFile.getString("queryCompleteTime"), signFile.getString("publicKeyFingerprint")); byte[] signatureContent = Hex.decodeHex(signFile.getString("hashSignature")); // Transform the PKCS#1 formatted public key to x.509 format. RSAPublicKey rsaPublicKey = RSAPublicKey.getInstance(pkcs1PublicKeyBytes); AlgorithmIdentifier rsaEncryption = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, null); SubjectPublicKeyInfo publicKeyInfo = new SubjectPublicKeyInfo(rsaEncryption, rsaPublicKey); // Create the PublicKey object needed for the signature validation PublicKey publicKey = KeyFactory.getInstance("RSA", "BC") .generatePublic(new X509EncodedKeySpec(publicKeyInfo.getEncoded())); // Verify signature Signature signature = Signature.getInstance("SHA256withRSA", "BC"); signature.initVerify(publicKey); signature.update(hashListString.getBytes("UTF-8")); if (signature.verify(signatureContent)) { System.out.println("Sign file signature is valid."); } else { System.err.println("Sign file signature failed validation."); } System.out.println("Sign file validation completed."); } }