Skip to main content

Ensure IAM Inline Policies Do Not Allow Full KMS Access

Overview

This check identifies IAM inline policies that grant unrestricted access to AWS Key Management Service (KMS) using the wildcard action kms:*. KMS is a critical security service that manages encryption keys protecting your sensitive data. Policies should follow the principle of least privilege, granting only the specific permissions needed.

Risk

Allowing kms:* permissions creates serious security risks:

  • Data exposure: Anyone with this access can decrypt any data protected by your KMS keys
  • Key tampering: Attackers could modify key policies or create unauthorized grants
  • Service disruption: Malicious actors could disable or delete encryption keys, breaking applications that depend on them

KMS is a foundational security service. Overly permissive access here can undermine encryption protections across your entire AWS environment.

Remediation Steps

Prerequisites

You need IAM permissions to view and modify inline policies (such as iam:GetUserPolicy, iam:PutUserPolicy, or equivalent for roles and groups).

AWS Console Method

  1. Open the IAM Console
  2. In the left navigation, click Users, Roles, or Groups (depending on where the policy is attached)
  3. Select the entity flagged by Prowler
  4. Expand the Permissions section
  5. Find the inline policy that contains kms:* and click Edit
  6. Switch to the JSON tab
  7. Replace "Action": "kms:*" with only the specific actions needed. Common examples:
    • For encryption/decryption: "kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey"
    • For key administration: "kms:CreateKey", "kms:DescribeKey", "kms:EnableKey"
  8. Restrict the Resource field to specific key ARNs instead of "*"
  9. Click Save changes

Example of a least-privilege policy:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowEncryptDecrypt",
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey",
"kms:GenerateDataKeyWithoutPlaintext"
],
"Resource": "arn:aws:kms:us-east-1:123456789012:key/your-key-id"
}
]
}
AWS CLI (optional)

List inline policies for a user:

aws iam list-user-policies \
--user-name <your-user-name> \
--region us-east-1

View the current policy:

aws iam get-user-policy \
--user-name <your-user-name> \
--policy-name <policy-name> \
--region us-east-1

Update the policy with least-privilege permissions:

First, create a policy file named kms-least-privilege.json:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowEncryptDecrypt",
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey",
"kms:GenerateDataKeyWithoutPlaintext"
],
"Resource": "arn:aws:kms:us-east-1:123456789012:key/your-key-id"
}
]
}

Then apply it:

aws iam put-user-policy \
--user-name <your-user-name> \
--policy-name <policy-name> \
--policy-document file://kms-least-privilege.json \
--region us-east-1

For roles, use equivalent commands:

# List inline policies
aws iam list-role-policies --role-name <your-role-name> --region us-east-1

# Get policy
aws iam get-role-policy --role-name <your-role-name> --policy-name <policy-name> --region us-east-1

# Update policy
aws iam put-role-policy \
--role-name <your-role-name> \
--policy-name <policy-name> \
--policy-document file://kms-least-privilege.json \
--region us-east-1

For groups, use equivalent commands:

# List inline policies
aws iam list-group-policies --group-name <your-group-name> --region us-east-1

# Get policy
aws iam get-group-policy --group-name <your-group-name> --policy-name <policy-name> --region us-east-1

# Update policy
aws iam put-group-policy \
--group-name <your-group-name> \
--policy-name <policy-name> \
--policy-document file://kms-least-privilege.json \
--region us-east-1
CloudFormation (optional)

This template creates an IAM role with a properly scoped KMS inline policy:

AWSTemplateFormatVersion: '2010-09-09'
Description: IAM role with least-privilege KMS inline policy

Parameters:
KmsKeyArn:
Type: String
Description: ARN of the KMS key to grant access to

Resources:
LeastPrivilegeRole:
Type: AWS::IAM::Role
Properties:
RoleName: KmsLeastPrivilegeRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: KmsLeastPrivilegePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowEncryptDecrypt
Effect: Allow
Action:
- kms:Encrypt
- kms:Decrypt
- kms:GenerateDataKey
- kms:GenerateDataKeyWithoutPlaintext
Resource: !Ref KmsKeyArn

Outputs:
RoleArn:
Description: ARN of the IAM role
Value: !GetAtt LeastPrivilegeRole.Arn

Deploy the stack:

aws cloudformation deploy \
--template-file template.yaml \
--stack-name kms-least-privilege-role \
--parameter-overrides KmsKeyArn=arn:aws:kms:us-east-1:123456789012:key/your-key-id \
--capabilities CAPABILITY_NAMED_IAM \
--region us-east-1
Terraform (optional)

This configuration creates an IAM role with a properly scoped KMS inline policy:

variable "kms_key_arn" {
description = "ARN of the KMS key to grant access to"
type = string
}

resource "aws_iam_role" "kms_least_privilege" {
name = "KmsLeastPrivilegeRole"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})

inline_policy {
name = "KmsLeastPrivilegePolicy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowEncryptDecrypt"
Effect = "Allow"
Action = [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey",
"kms:GenerateDataKeyWithoutPlaintext"
]
Resource = var.kms_key_arn
}
]
})
}
}

output "role_arn" {
description = "ARN of the IAM role"
value = aws_iam_role.kms_least_privilege.arn
}

Apply the configuration:

terraform init
terraform plan -var="kms_key_arn=arn:aws:kms:us-east-1:123456789012:key/your-key-id"
terraform apply -var="kms_key_arn=arn:aws:kms:us-east-1:123456789012:key/your-key-id"

Verification

After making changes, verify the fix:

  1. Return to the IAM Console and review the updated policy
  2. Confirm the policy no longer contains kms:*
  3. Re-run the Prowler check to confirm the finding is resolved:
prowler aws --checks iam_inline_policy_no_full_access_to_kms

Additional Resources

Notes

  • Prefer managed policies: AWS recommends using managed policies over inline policies when possible for easier maintenance and reuse
  • Separation of duties: Consider separating key administrators (who manage keys) from key users (who use keys for encryption/decryption)
  • Use conditions: Add IAM policy conditions to further restrict access by encryption context, calling service, or source IP
  • Test thoroughly: Before removing permissions, ensure applications and services that depend on KMS access continue to function correctly
  • Consider Service Control Policies: Use SCPs at the organization level to prevent overly permissive KMS policies from being created in the first place