コンテンツにスキップ

KMS キー

検証日: 2026-04-04 / リージョン: ap-northeast-1

概要

IAM Access Analyzer が KMS キーの外部アクセスをどのように検出するかを検証する。

KMS キーはキーポリシーにより外部プリンシパルへのアクセスを許可できる。S3 の Block Public Access のような明示的なブロック機構は存在しないが、デフォルトキーポリシーは自アカウントの root にのみ kms:* を許可する保守的な設定であり、意図的にキーポリシーを変更しない限り外部アクセスは発生しない。キーポリシーに Principal: "*" を設定するとパブリックアクセスとして検出され、特定の外部アカウントを設定するとクロスアカウントアクセスとして検出される。

本記事ではキーポリシーによる外部アクセスを検証する。

KMS API は署名付きリクエストが必須であり、S3 のような匿名 HTTP アクセスはできない。KMS における「パブリックアクセス」とは、全 AWS プリンシパルからのアクセスを意味する。

KMS キーの外部アクセス制御レイヤー

レイヤーKMS キーの状況
デフォルト保護なし(ただしデフォルトキーポリシーは自アカウントのみに制限)
予防(RCP)CT.KMS.PV.7(組織外プリンシパルによる KMS アクセスを拒否)
Security Hub CSPMKMS.5(パブリックアクセスのチェック)
IAM Access Analyzerパブリック + クロスアカウントの到達可能性を分析

結果

  • デフォルトキーポリシー: ポリシー指定なしで作成した KMS キーのデフォルトキーポリシーは、自アカウントの root にのみ kms:* を許可する保守的な設定であり、外部アクセスは許可されていない
  • パブリックアクセスの検出: キーポリシーに Principal: "*" を設定すると、isPublic: true の finding が生成された。IAM ロールの信頼ポリシーでは "Principal": "*" がエラーになるが、KMS のキーポリシーではそのまま受け付けられる
  • クロスアカウントアクセスの検出: 組織外アカウントをキーポリシーに設定すると、isPublic: false の finding が生成された。principal に組織外アカウント ID が特定された
  • 予防コントロール(CT.KMS.PV.7)との連動: RCP 有効時は組織外からの Encrypt がパブリック・クロスアカウントの両方でブロックされた。再スキャン後、resourceControlPolicyRestrictionAPPLICABLE に変わった
  • キーの無効化・削除と finding の関係: disable-keyschedule-key-deletion では finding は RESOLVED にならない。キーポリシーから外部アクセスの許可を削除することで RESOLVED になる。IAM Access Analyzer はキーの状態ではなくキーポリシーの内容を評価している

検証環境

検証環境の構成図

本記事のコマンドは、--profile 指定がない場合は Workload アカウントで実行する。IAM Access Analyzer の finding 確認は Audit アカウントで実行する。

検証の流れ

    flowchart LR
    A[1. 既存 finding の<br>確認] --> B[2. テスト用キー<br>作成]
    B --> C[3. パブリックアクセス<br>検出の確認]
    C --> D[4. クロスアカウント<br>アクセス検出]
    D --> E[5. 予防コントロール<br>との連動]
    E --> F[6. クリーンアップ]
  

1. 既存 finding の確認

テスト用キーを作成する前に、デフォルトキーポリシーと既存の KMS キー finding を確認する。

デフォルトキーポリシーの確認

KMS キーをポリシー指定なしで作成した場合のデフォルトキーポリシーを確認する。

aws kms create-key \
  --description "iaa-kms-default-policy-check"
aws kms get-key-policy \
  --key-id <確認用キー ID> \
  --policy-name default \
  --output json | jq -r '.Policy' | jq .
{
    "Version": "2012-10-17",
    "Id": "key-default-1",
    "Statement": [
        {
            "Sid": "Enable IAM User Permissions",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<Workload アカウント ID>:root"
            },
            "Action": "kms:*",
            "Resource": "*"
        }
    ]
}

デフォルトキーポリシーは自アカウントの root にのみ kms:* を許可しており、外部アクセスは許可されていない。確認後、キーを削除する。

aws kms schedule-key-deletion \
  --key-id <確認用キー ID> \
  --pending-window-in-days 7

既存 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::KMS::Key"]}}' \
  --query 'findings[].{id:id,resource:resource,status:status}' \
  --region ap-northeast-1 \
  --profile Audit
[]

2. テスト用キーの作成

組織外アカウントへのアクセスを許可するキーを作成する。検証目的のみで行い、ステップ 6 で削除する。

2 つのテスト用 KMS キーを作成する。キーポリシーには自アカウントの管理用ステートメント(kms:*)を必ず含める。KMS はキーポリシーが唯一の管理手段であり、管理用ステートメントを含めないとキーの管理権限を失う。

パブリックアクセス検証用キー

キーポリシーに Principal: "*" を設定する。

IAM ロールの信頼ポリシーでは "Principal": "*" がエラーになり "Principal": {"AWS": "*"} の形式が必要だったが(IAM ロールの検証を参照)、KMS のキーポリシーでは "Principal": "*" がそのまま受け付けられる。
aws kms create-key \
  --description "iaa-kms-public-test" \
  --policy '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "AllowRootAccount",
        "Effect": "Allow",
        "Principal": {"AWS": "arn:aws:iam::<Workload アカウント ID>:root"},
        "Action": "kms:*",
        "Resource": "*"
      },
      {
        "Sid": "AllowPublicEncrypt",
        "Effect": "Allow",
        "Principal": "*",
        "Action": ["kms:Encrypt", "kms:Decrypt"],
        "Resource": "*"
      }
    ]
  }'
{
    "KeyMetadata": {
        "KeyId": "<パブリックキー ID>",
        "Arn": "arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<パブリックキー ID>",
        "KeyState": "Enabled"
    }
}

エイリアスを設定する。

aws kms create-alias \
  --alias-name alias/iaa-kms-public-test \
  --target-key-id <パブリックキー ID>
(出力なし)

クロスアカウントアクセス検証用キー

キーポリシーに組織外アカウントを設定する。

aws kms create-key \
  --description "iaa-kms-crossaccount-test" \
  --policy '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "AllowRootAccount",
        "Effect": "Allow",
        "Principal": {"AWS": "arn:aws:iam::<Workload アカウント ID>:root"},
        "Action": "kms:*",
        "Resource": "*"
      },
      {
        "Sid": "AllowCrossAccountAccess",
        "Effect": "Allow",
        "Principal": {"AWS": "arn:aws:iam::<組織外アカウント ID>:root"},
        "Action": ["kms:Encrypt", "kms:Decrypt"],
        "Resource": "*"
      }
    ]
  }'
{
    "KeyMetadata": {
        "KeyId": "<クロスアカウントキー ID>",
        "Arn": "arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<クロスアカウントキー ID>",
        "KeyState": "Enabled"
    }
}

エイリアスを設定する。

aws kms create-alias \
  --alias-name alias/iaa-kms-crossaccount-test \
  --target-key-id <クロスアカウントキー ID>
(出力なし)

3. パブリックアクセスの検出

ポリシー変更後、IAM Access Analyzer が finding を生成・更新するまで最大 30 分かかる場合がある。

パブリックアクセス検証用キー(Principal: "*")の finding を確認する。KMS キーの finding はキー ARN で記録されるため、エイリアス名ではフィルタできない。

aws accessanalyzer list-findings-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["<パブリックキー ID>"]}}' \
  --query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
  --region ap-northeast-1 \
  --profile Audit
[
    {
        "id": "<パブリックキー finding ID>",
        "resource": "arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<パブリックキー ID>",
        "resourceType": "AWS::KMS::Key",
        "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": ["kms:Decrypt", "kms:Encrypt"],
                "condition": {},
                "isPublic": true,
                "principal": {"AWS": "*"},
                "resourceControlPolicyRestriction": "NOT_APPLICABLE"
            }
        }
    ],
    "resource": "arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<パブリックキー ID>",
    "status": "ACTIVE",
    "resourceType": "AWS::KMS::Key",
    "findingType": "ExternalAccess",
    "resourceOwnerAccount": "<Workload アカウント ID>",
    "id": "<パブリックキー finding ID>"
}

以下を確認する。

  • isPublictrue
  • principal{"AWS": "*"}
  • actionkms:Decrypt, kms:Encrypt が含まれる
  • resourceControlPolicyRestrictionNOT_APPLICABLE(CT.KMS.PV.7 無効時)

組織外アカウントからの実際のアクセス確認

組織外アカウントからパブリックキーへの Encrypt が成功することを確認する。

echo "test" > /tmp/plaintext.txt
aws kms encrypt \
  --key-id arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<パブリックキー ID> \
  --plaintext fileb:///tmp/plaintext.txt \
  --query '{KeyId:KeyId}' \
  --profile <組織外プロファイル> \
  --region ap-northeast-1
{
    "KeyId": "arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<パブリックキー ID>"
}

4. クロスアカウントアクセスの検出

クロスアカウントアクセス検証用キーの 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"]}, "resource": {"contains": ["<クロスアカウントキー ID>"]}}' \
  --query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
  --region ap-northeast-1 \
  --profile Audit
[
    {
        "id": "<クロスアカウントキー finding ID>",
        "resource": "arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<クロスアカウントキー ID>",
        "resourceType": "AWS::KMS::Key",
        "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": ["kms:Decrypt", "kms:Encrypt"],
                "condition": {},
                "isPublic": false,
                "principal": {"AWS": "<組織外アカウント ID>"},
                "resourceControlPolicyRestriction": "NOT_APPLICABLE"
            }
        }
    ],
    "resource": "arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<クロスアカウントキー ID>",
    "status": "ACTIVE",
    "resourceType": "AWS::KMS::Key",
    "findingType": "ExternalAccess",
    "resourceOwnerAccount": "<Workload アカウント ID>",
    "id": "<クロスアカウントキー finding ID>"
}

以下を確認する。

  • isPublicfalse
  • principal に組織外アカウント ID が特定されている
  • actionkms:Decrypt, kms:Encrypt が含まれる
  • resourceControlPolicyRestrictionNOT_APPLICABLE(CT.KMS.PV.7 無効時)

組織外アカウントからの実際のアクセス確認

組織外アカウントからクロスアカウントキーへの Encrypt が成功することを確認する。

aws kms encrypt \
  --key-id arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<クロスアカウントキー ID> \
  --plaintext fileb:///tmp/plaintext.txt \
  --query '{KeyId:KeyId}' \
  --profile <組織外プロファイル> \
  --region ap-northeast-1
{
    "KeyId": "arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<クロスアカウントキー ID>"
}

5. 予防コントロール(CT.KMS.PV.7)との連動

本ステップは CT.KMS.PV.7(RCP: 組織外プリンシパルによる KMS アクセスを拒否)が有効な環境で実施する。CT.KMS.PV.7 の詳細は CT.KMS.PV.7 の検証記事 を参照。

CT.KMS.PV.7 の有効化確認

CT.KMS.PV.7 が有効であることを確認する。

aws controltower list-enabled-controls \
  --target-identifier <対象 OU ARN> \
  --query "enabledControls[?controlIdentifier=='arn:aws:controlcatalog:::control/eolw7feyvr8b4l2lfhp3bneou'].{controlIdentifier:controlIdentifier,statusSummary:statusSummary}" \
  --profile Master
[]

無効の場合は有効化する。

aws controltower enable-control \
  --control-identifier arn:aws:controlcatalog:::control/eolw7feyvr8b4l2lfhp3bneou \
  --target-identifier <対象 OU ARN> \
  --profile Master
{
    "operationIdentifier": "<オペレーション ID>"
}

組織外アカウントからのアクセス確認

CT.KMS.PV.7 が有効な状態で、組織外アカウントからクロスアカウントテスト用キーへの Encrypt を試み、RCP によりブロックされることを確認する。

aws kms encrypt \
  --key-id arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<クロスアカウントキー ID> \
  --plaintext fileb:///tmp/plaintext.txt \
  --profile <組織外プロファイル> \
  --region ap-northeast-1
An error occurred (AccessDeniedException) when calling the Encrypt operation: User: arn:aws:iam::<組織外アカウント ID>:user/<組織外ユーザー名> is not authorized to perform: kms:Encrypt on this resource with an explicit deny in a resource control policy

パブリックキーへの Encrypt も同様にブロックされることを確認する。

aws kms encrypt \
  --key-id arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<パブリックキー ID> \
  --plaintext fileb:///tmp/plaintext.txt \
  --profile <組織外プロファイル> \
  --region ap-northeast-1
An error occurred (AccessDeniedException) when calling the Encrypt operation: User: arn:aws:iam::<組織外アカウント ID>:user/<組織外ユーザー名> is not authorized to perform: kms:Encrypt on this resource with an explicit deny in a resource control policy

resourceControlPolicyRestriction の確認

リソースの再スキャンを実行して、RCP の反映を確認する。再スキャンにより旧 finding は RESOLVED になり、新しい finding ID で再生成される。

aws accessanalyzer start-resource-scan \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --resource-arn arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<クロスアカウントキー ID> \
  --resource-owner-account <Workload アカウント ID> \
  --region ap-northeast-1 \
  --profile Audit
(出力なし)

パブリックキーも再スキャンする。

aws accessanalyzer start-resource-scan \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --resource-arn arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<パブリックキー ID> \
  --resource-owner-account <Workload アカウント ID> \
  --region ap-northeast-1 \
  --profile Audit
(出力なし)

再スキャン後の 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::KMS::Key"]}}' \
  --query 'findings[].{id:id,status:status}' \
  --region ap-northeast-1 \
  --profile Audit
[
    {
        "id": "<新クロスアカウントキー finding ID>",
        "status": "ACTIVE"
    },
    {
        "id": "<新パブリックキー finding ID>",
        "status": "ACTIVE"
    }
]

finding 詳細を確認し、resourceControlPolicyRestrictionAPPLICABLE に変わっていることを確認する。

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": ["kms:Decrypt", "kms:Encrypt"],
                "isPublic": false,
                "principal": {"AWS": "<組織外アカウント ID>"},
                "resourceControlPolicyRestriction": "APPLICABLE"
            }
        }
    ],
    "status": "ACTIVE",
    "id": "<新クロスアカウントキー finding ID>"
}
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": ["kms:Decrypt", "kms:Encrypt"],
                "isPublic": true,
                "principal": {"AWS": "*"},
                "resourceControlPolicyRestriction": "APPLICABLE"
            }
        }
    ],
    "status": "ACTIVE",
    "id": "<新パブリックキー finding ID>"
}

6. クリーンアップ

KMS キーは即時削除できない。schedule-key-deletion で削除をスケジュールすると、最短 7 日の待機期間の後に削除される。disable-keyschedule-key-deletion では IAM Access Analyzer の finding は RESOLVED にならない(キーポリシー自体は変わらないため)。finding を RESOLVED にするには、キーポリシーから外部アクセスの許可を削除する必要がある。

CT.KMS.PV.7 の無効化

ステップ 5 で CT.KMS.PV.7 を有効化した場合のみ無効化する。元々有効な環境ではそのまま有効にしておくこと。
aws controltower disable-control \
  --control-identifier arn:aws:controlcatalog:::control/eolw7feyvr8b4l2lfhp3bneou \
  --target-identifier <対象 OU ARN> \
  --profile Master
{
    "operationIdentifier": "<オペレーション ID>"
}

キーポリシーから外部アクセスを削除

キーポリシーを自アカウントの管理用ステートメントのみに変更する。

aws kms put-key-policy \
  --key-id <パブリックキー ID> \
  --policy-name default \
  --policy '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "AllowRootAccount",
        "Effect": "Allow",
        "Principal": {"AWS": "arn:aws:iam::<Workload アカウント ID>:root"},
        "Action": "kms:*",
        "Resource": "*"
      }
    ]
  }'
(出力なし)
aws kms put-key-policy \
  --key-id <クロスアカウントキー ID> \
  --policy-name default \
  --policy '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "AllowRootAccount",
        "Effect": "Allow",
        "Principal": {"AWS": "arn:aws:iam::<Workload アカウント ID>:root"},
        "Action": "kms:*",
        "Resource": "*"
      }
    ]
  }'
(出力なし)

finding の確認

キーポリシー変更後、finding が RESOLVED になることを確認する。

aws accessanalyzer list-findings-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --filter '{"resourceType": {"eq": ["AWS::KMS::Key"]}}' \
  --query 'findings[].{id:id,resource:resource,status:status}' \
  --region ap-northeast-1 \
  --profile Audit
[]

エイリアスの削除

aws kms delete-alias \
  --alias-name alias/iaa-kms-public-test
(出力なし)
aws kms delete-alias \
  --alias-name alias/iaa-kms-crossaccount-test
(出力なし)

KMS キーの削除スケジュール

aws kms schedule-key-deletion \
  --key-id <パブリックキー ID> \
  --pending-window-in-days 7
{
    "KeyId": "arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<パブリックキー ID>",
    "KeyState": "PendingDeletion",
    "DeletionDate": "<削除予定日>"
}
aws kms schedule-key-deletion \
  --key-id <クロスアカウントキー ID> \
  --pending-window-in-days 7
{
    "KeyId": "arn:aws:kms:ap-northeast-1:<Workload アカウント ID>:key/<クロスアカウントキー ID>",
    "KeyState": "PendingDeletion",
    "DeletionDate": "<削除予定日>"
}

Amazonアソシエイトリンク