KMS キー
概要
IAM Access Analyzer が KMS キーの外部アクセスをどのように検出するかを検証する。
KMS キーはキーポリシーにより外部プリンシパルへのアクセスを許可できる。S3 の Block Public Access のような明示的なブロック機構は存在しないが、デフォルトキーポリシーは自アカウントの root にのみ kms:* を許可する保守的な設定であり、意図的にキーポリシーを変更しない限り外部アクセスは発生しない。キーポリシーに Principal: "*" を設定するとパブリックアクセスとして検出され、特定の外部アカウントを設定するとクロスアカウントアクセスとして検出される。
本記事ではキーポリシーによる外部アクセスを検証する。
KMS キーの外部アクセス制御レイヤー
| レイヤー | KMS キーの状況 |
|---|---|
| デフォルト保護 | なし(ただしデフォルトキーポリシーは自アカウントのみに制限) |
| 予防(RCP) | CT.KMS.PV.7(組織外プリンシパルによる KMS アクセスを拒否) |
| Security Hub CSPM | KMS.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 がパブリック・クロスアカウントの両方でブロックされた。再スキャン後、
resourceControlPolicyRestrictionがAPPLICABLEに変わった - キーの無効化・削除と finding の関係:
disable-keyやschedule-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. テスト用キーの作成
2 つのテスト用 KMS キーを作成する。キーポリシーには自アカウントの管理用ステートメント(kms:*)を必ず含める。KMS はキーポリシーが唯一の管理手段であり、管理用ステートメントを含めないとキーの管理権限を失う。
パブリックアクセス検証用キー
キーポリシーに Principal: "*" を設定する。
"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. パブリックアクセスの検出
パブリックアクセス検証用キー(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>"
}以下を確認する。
isPublicがtrueprincipalが{"AWS": "*"}actionにkms:Decrypt,kms:Encryptが含まれるresourceControlPolicyRestrictionがNOT_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>"
}以下を確認する。
isPublicがfalseprincipalに組織外アカウント ID が特定されているactionにkms:Decrypt,kms:Encryptが含まれるresourceControlPolicyRestrictionがNOT_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 の有効化確認
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-1An 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-1An 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 policyresourceControlPolicyRestriction の確認
リソースの再スキャンを実行して、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 詳細を確認し、resourceControlPolicyRestriction が APPLICABLE に変わっていることを確認する。
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. クリーンアップ
schedule-key-deletion で削除をスケジュールすると、最短 7 日の待機期間の後に削除される。disable-key や schedule-key-deletion では IAM Access Analyzer の finding は RESOLVED にならない(キーポリシー自体は変わらないため)。finding を RESOLVED にするには、キーポリシーから外部アクセスの許可を削除する必要がある。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": "<削除予定日>"
}