Back to Checks
aws

Custom IAM policy does not allow 'kms:*' privileges

iam_policy_no_full_access_to_kms
Service: iamSeverity: medium
OfficialBuilt-inVerified
Categories:identity-access
Resource Type:AwsIamPolicy

Customer-managed IAM policies are examined for statements that grant AWS KMS full access using kms:*. The focus is on policies allowing service-wide actions rather than narrowly scoped, key-specific permissions.

Risk

Allowing kms:* lets principals decrypt data, change key policies, and disable or delete keys. Impact: Confidentiality-unauthorized decryption; Integrity-manipulation of cryptographic controls; Availability-data unreadable if keys are disabled/deleted. It can also enable privilege escalation.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler aws --checks iam_policy_no_full_access_to_kms

Recommendation

Adopt least privilege and separation of duties:

  • Replace kms:* with only needed actions scoped to specific key ARNs
  • Apply policy conditions (e.g., kms:ViaService) and guardrails (permissions boundaries/SCPs)
  • Monitor KMS usage and refine access based on activity

Remediation

CLI

aws iam create-policy-version --policy-arn <POLICY_ARN> --policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["kms:Encrypt"],"Resource":"arn:aws:kms:<REGION>:<ACCOUNT_ID>:key/<KEY_ID>"}]}' --set-as-default

Terraform

# Customer managed policy without kms:* full access
resource "aws_iam_policy" "<example_resource_name>" {
  name   = "<example_resource_name>"
  policy = jsonencode({
    Version   = "2012-10-17"
    Statement = [{
      Effect   = "Allow"
      Action   = ["kms:Encrypt"]  # FIX: remove 'kms:*'; allow only specific KMS action
      Resource = "arn:aws:kms:<REGION>:<ACCOUNT_ID>:key/<example_resource_id>"  # FIX: scope to a specific key
    }]
  })
}

NativeIaC

# CloudFormation: customer managed policy without kms:* full access
Resources:
  <example_resource_name>:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - kms:Encrypt  # FIX: remove 'kms:*'; allow only specific KMS action
            Resource: arn:aws:kms:<REGION>:<ACCOUNT_ID>:key/<example_resource_id>  # FIX: scope to a specific key

Other

1In the AWS Console, open IAM > Policies
2Find the custom policy that allows kms:* and choose Edit policy > JSON
3Replace any "Action": "kms:*" (or ["kms:*"]) with only required actions (e.g., ["kms:Encrypt"]) and, if possible, set "Resource" to a specific key ARN
4Save changes (a new default policy version is created)
5Re-run the check to confirm it passes

Check Code

from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.iam.iam_client import iam_client
from prowler.providers.aws.services.iam.lib.policy import check_full_service_access

critical_service = "kms"


class iam_policy_no_full_access_to_kms(Check):
    def execute(self) -> Check_Report_AWS:
        findings = []
        for policy in iam_client.policies.values():
            # Check only custom policies
            if policy.type == "Custom":
                if not policy.attached and not iam_client.provider.scan_unused_services:
                    continue
                report = Check_Report_AWS(metadata=self.metadata(), resource=policy)
                report.region = iam_client.region
                report.status = "PASS"
                report.status_extended = f"Custom Policy {policy.name} does not allow '{critical_service}:*' privileges."
                if policy.document and check_full_service_access(
                    critical_service, policy.document
                ):
                    report.status = "FAIL"
                    report.status_extended = f"Custom Policy {policy.name} allows '{critical_service}:*' privileges."

                findings.append(report)
        return findings