Control: 5 KMS keys should not be publicly accessible
Description
This control checks whether an AWS KMS key is publicly accessible. The control fails if the KMS key is publicly accessible.
Implementing least privilege access is fundamental to reducing security risk and the impact of errors or malicious intent. If the key policy for an AWS KMS key allows access from external accounts, third parties might be able to encrypt and decrypt data by using the key. This could result in an internal or external threat exfiltrating data from AWS services that use the key.
Remediation
For information about updating the key policy for an AWS KMS key, see Key policies in AWS KMS in the AWS Key Management Service Developer Guide.
Usage
Run the control in your terminal:
powerpipe control run aws_compliance.control.foundational_security_kms_5
Snapshot and share results via Turbot Pipes:
powerpipe loginpowerpipe control run aws_compliance.control.foundational_security_kms_5 --share
SQL
This control uses a named query:
with wildcard_action_policies as ( select arn, count(*) as statements_num from aws_kms_key, jsonb_array_elements(policy_std -> 'Statement') as s where s ->> 'Effect' = 'Allow' -- kms:CallerAccount and s -> 'Condition' -> 'StringEquals' -> 'kms:calleraccount' is null and s -> 'Condition' -> 'StringEqualsIgnoreCase' -> 'kms:calleraccount' is null and ( s -> 'Condition' -> 'StringLike' -> 'kms:calleraccount' is null or s -> 'Condition' -> 'StringLike' -> 'kms:calleraccount' ? '*' ) -- aws:SourceOwner and s -> 'Condition' -> 'StringEquals' -> 'aws:sourceowner' is null and s -> 'Condition' -> 'StringEqualsIgnoreCase' -> 'aws:sourceowner' is null and ( s -> 'Condition' -> 'StringLike' -> 'aws:sourceowner' is null or s -> 'Condition' -> 'StringLike' -> 'aws:sourceowner' ? '*' ) -- aws:SourceAccount and s -> 'Condition' -> 'StringEquals' -> 'aws:sourceaccount' is null and s -> 'Condition' -> 'StringEqualsIgnoreCase' -> 'aws:sourceaccount' is null and ( s -> 'Condition' -> 'StringLike' -> 'aws:sourceaccount' is null or s -> 'Condition' -> 'StringLike' -> 'aws:sourceaccount' ? '*' ) -- aws:PrincipalOrgID and s -> 'Condition' -> 'StringEquals' -> 'aws:principalorgid' is null and s -> 'Condition' -> 'StringEqualsIgnoreCase' -> 'aws:principalorgid' is null and ( s -> 'Condition' -> 'StringLike' -> 'aws:principalorgid' is null or s -> 'Condition' -> 'StringLike' -> 'aws:principalorgid' ? '*' ) -- aws:PrincipalAccount and s -> 'Condition' -> 'StringEquals' -> 'aws:principalaccount' is null and s -> 'Condition' -> 'StringEqualsIgnoreCase' -> 'aws:principalaccount' is null and ( s -> 'Condition' -> 'StringLike' -> 'aws:principalaccount' is null or s -> 'Condition' -> 'StringLike' -> 'aws:principalaccount' ? '*' ) -- aws:PrincipalArn and s -> 'Condition' -> 'StringEquals' -> 'aws:principalarn' is null and s -> 'Condition' -> 'StringEqualsIgnoreCase' -> 'aws:principalarn' is null and ( s -> 'Condition' -> 'StringLike' -> 'aws:principalarn' is null or s -> 'Condition' -> 'StringLike' -> 'aws:principalarn' ? '*' ) and ( s -> 'Condition' -> 'ArnEquals' -> 'aws:principalarn' is null or s -> 'Condition' -> 'ArnEquals' -> 'aws:principalarn' ? '*' ) and ( s -> 'Condition' -> 'ArnLike' -> 'aws:principalarn' is null or s -> 'Condition' -> 'ArnLike' -> 'aws:principalarn' ? '*' ) -- aws:SourceArn and s -> 'Condition' -> 'StringEquals' -> 'aws:sourcearn' is null and s -> 'Condition' -> 'StringEqualsIgnoreCase' -> 'aws:sourcearn' is null and ( s -> 'Condition' -> 'StringLike' -> 'aws:sourcearn' is null or s -> 'Condition' -> 'StringLike' -> 'aws:sourcearn' ? '*' ) and ( s -> 'Condition' -> 'ArnEquals' -> 'aws:sourcearn' is null or s -> 'Condition' -> 'ArnEquals' -> 'aws:sourcearn' ? '*' ) and ( s -> 'Condition' -> 'ArnLike' -> 'aws:sourcearn' is null or s -> 'Condition' -> 'ArnLike' -> 'aws:sourcearn' ? '*' ) and ( s -> 'Principal' -> 'AWS' = '["*"]' or s ->> 'Principal' = '*' ) group by arn)select k.arn as resource, case when p.arn is null then 'ok' else 'alarm' end status, case when p.arn is null then title || ' does not allow public access.' else title || ' contains ' || coalesce(p.statements_num, 0) || ' statements that allow public access.' end as reason , k.region, k.account_idfrom aws_kms_key as k left join wildcard_action_policies as p on p.arn = k.arnwhere key_manager = 'CUSTOMER';