KMS Key Not Publicly Accessible
Overview
This check verifies that your AWS KMS (Key Management Service) keys are not publicly accessible. A KMS key becomes publicly accessible when its key policy grants permissions to everyone (using a wildcard * as the principal) without any conditions to restrict access.
KMS keys are used to encrypt sensitive data across AWS services. Keeping them private ensures only authorized users and services can encrypt or decrypt your data.
Risk
If a KMS key is publicly accessible:
- Unauthorized access: Anyone on the internet could potentially use the key to encrypt or decrypt data
- Data breaches: Attackers could decrypt sensitive information protected by the key
- Compliance violations: Public KMS keys violate PCI DSS, GDPR, HIPAA, and other regulatory requirements
- Resource abuse: Bad actors could use your key for their own encryption operations, potentially incurring costs
Severity: Critical
Remediation Steps
Prerequisites
You need:
- Access to the AWS Console with permissions to view and modify KMS key policies
- The key ID or alias of the affected KMS key
AWS CLI setup (optional)
If you prefer using the command line, ensure you have:
- AWS CLI installed and configured
- IAM permissions for
kms:GetKeyPolicyandkms:PutKeyPolicy
Test your access:
aws kms list-keys --region us-east-1 --query 'Keys[0].KeyId'
AWS Console Method
-
Open the KMS Console
- Go to the AWS KMS Console
- Make sure you're in the correct region (us-east-1 or the region where your key exists)
-
Find the affected key
- Click Customer managed keys in the left navigation
- Locate the key flagged by Prowler (use the key ID from the finding)
-
View the current key policy
- Click on the key alias or key ID to open its details
- Select the Key policy tab
- Click Switch to policy view to see the full JSON policy
-
Identify the problem
- Look for policy statements where:
"Principal"is set to"*"or{"AWS": "*"}"Effect"is"Allow"- There is no
"Condition"block restricting access
- Look for policy statements where:
-
Fix the key policy
- Click Edit
- Replace the wildcard principal with specific account or role ARNs:
Before: "Principal": "*"
After: "Principal": {"AWS": "arn:aws:iam::123456789012:root"} - Or add a condition to restrict access to your account:
"Condition": {
"StringEquals": {
"kms:CallerAccount": "123456789012"
}
} - Click Save changes
AWS CLI (optional)
Step 1: List your KMS keys
aws kms list-keys \
--region us-east-1 \
--query 'Keys[*].KeyId' \
--output table
Step 2: Get the current key policy
aws kms get-key-policy \
--region us-east-1 \
--key-id <your-key-id> \
--policy-name default \
--output text \
--query 'Policy' | jq .
Step 3: Create a secure policy file
Save this to a file named secure-key-policy.json (replace 123456789012 with your AWS account ID):
{
"Version": "2012-10-17",
"Id": "secure-key-policy",
"Statement": [
{
"Sid": "AllowRootAccountFullAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "AllowKeyAdministration",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/KMSAdmin"
},
"Action": [
"kms:Create*",
"kms:Describe*",
"kms:Enable*",
"kms:List*",
"kms:Put*",
"kms:Update*",
"kms:Revoke*",
"kms:Disable*",
"kms:Get*",
"kms:Delete*",
"kms:TagResource",
"kms:UntagResource",
"kms:ScheduleKeyDeletion",
"kms:CancelKeyDeletion"
],
"Resource": "*"
},
{
"Sid": "AllowKeyUsage",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/KMSUser"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
}
]
}
Step 4: Apply the secure policy
aws kms put-key-policy \
--region us-east-1 \
--key-id <your-key-id> \
--policy-name default \
--policy file://secure-key-policy.json
CloudFormation (optional)
Use this template to create a new KMS key with a secure policy:
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Secure KMS Key with restricted key policy (no public access)'
Parameters:
KeyAdminArn:
Type: String
Description: 'ARN of the IAM user or role that will administer this key'
Default: 'arn:aws:iam::123456789012:role/KMSAdmin'
KeyUserArn:
Type: String
Description: 'ARN of the IAM user or role that will use this key for encryption/decryption'
Default: 'arn:aws:iam::123456789012:role/KMSUser'
Resources:
SecureKMSKey:
Type: AWS::KMS::Key
Properties:
Description: 'Secure KMS key with restricted access policy'
EnableKeyRotation: true
KeyPolicy:
Version: '2012-10-17'
Id: secure-key-policy
Statement:
- Sid: AllowRootAccountFullAccess
Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
Action: 'kms:*'
Resource: '*'
- Sid: AllowKeyAdministration
Effect: Allow
Principal:
AWS: !Ref KeyAdminArn
Action:
- 'kms:Create*'
- 'kms:Describe*'
- 'kms:Enable*'
- 'kms:List*'
- 'kms:Put*'
- 'kms:Update*'
- 'kms:Revoke*'
- 'kms:Disable*'
- 'kms:Get*'
- 'kms:Delete*'
- 'kms:TagResource'
- 'kms:UntagResource'
- 'kms:ScheduleKeyDeletion'
- 'kms:CancelKeyDeletion'
Resource: '*'
- Sid: AllowKeyUsage
Effect: Allow
Principal:
AWS: !Ref KeyUserArn
Action:
- 'kms:Encrypt'
- 'kms:Decrypt'
- 'kms:ReEncrypt*'
- 'kms:GenerateDataKey*'
- 'kms:DescribeKey'
Resource: '*'
Tags:
- Key: Name
Value: secure-kms-key
SecureKMSKeyAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: alias/secure-kms-key
TargetKeyId: !Ref SecureKMSKey
Outputs:
KeyId:
Description: 'The ID of the KMS key'
Value: !Ref SecureKMSKey
KeyArn:
Description: 'The ARN of the KMS key'
Value: !GetAtt SecureKMSKey.Arn
KeyAlias:
Description: 'The alias of the KMS key'
Value: !Ref SecureKMSKeyAlias
Deploy with:
aws cloudformation deploy \
--template-file kms-secure-policy.yaml \
--stack-name secure-kms-key \
--parameter-overrides \
KeyAdminArn=arn:aws:iam::123456789012:role/KMSAdmin \
KeyUserArn=arn:aws:iam::123456789012:role/KMSUser \
--region us-east-1
Terraform (optional)
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
data "aws_caller_identity" "current" {}
variable "key_admin_arn" {
description = "ARN of the IAM user or role that will administer this key"
type = string
default = ""
}
variable "key_user_arn" {
description = "ARN of the IAM user or role that will use this key for encryption/decryption"
type = string
default = ""
}
locals {
key_admin_arn = var.key_admin_arn != "" ? var.key_admin_arn : "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
key_user_arn = var.key_user_arn != "" ? var.key_user_arn : "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
resource "aws_kms_key" "secure_key" {
description = "Secure KMS key with restricted access policy"
enable_key_rotation = true
deletion_window_in_days = 30
policy = jsonencode({
Version = "2012-10-17"
Id = "secure-key-policy"
Statement = [
{
Sid = "AllowRootAccountFullAccess"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
Action = "kms:*"
Resource = "*"
},
{
Sid = "AllowKeyAdministration"
Effect = "Allow"
Principal = {
AWS = local.key_admin_arn
}
Action = [
"kms:Create*",
"kms:Describe*",
"kms:Enable*",
"kms:List*",
"kms:Put*",
"kms:Update*",
"kms:Revoke*",
"kms:Disable*",
"kms:Get*",
"kms:Delete*",
"kms:TagResource",
"kms:UntagResource",
"kms:ScheduleKeyDeletion",
"kms:CancelKeyDeletion"
]
Resource = "*"
},
{
Sid = "AllowKeyUsage"
Effect = "Allow"
Principal = {
AWS = local.key_user_arn
}
Action = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
]
Resource = "*"
}
]
})
tags = {
Name = "secure-kms-key"
}
}
resource "aws_kms_alias" "secure_key_alias" {
name = "alias/secure-kms-key"
target_key_id = aws_kms_key.secure_key.key_id
}
output "key_id" {
description = "The ID of the KMS key"
value = aws_kms_key.secure_key.key_id
}
output "key_arn" {
description = "The ARN of the KMS key"
value = aws_kms_key.secure_key.arn
}
output "key_alias" {
description = "The alias of the KMS key"
value = aws_kms_alias.secure_key_alias.name
}
Deploy with:
terraform init
terraform apply -var="key_admin_arn=arn:aws:iam::123456789012:role/KMSAdmin" \
-var="key_user_arn=arn:aws:iam::123456789012:role/KMSUser"
Verification
After making changes, verify the fix:
-
In the AWS Console:
- Return to the KMS key details page
- Check the Key policy tab
- Confirm no statements have
"Principal": "*"without conditions
-
Re-run Prowler:
prowler aws --checks kms_key_not_publicly_accessible --region us-east-1
CLI verification commands
Check the updated policy:
aws kms get-key-policy \
--region us-east-1 \
--key-id <your-key-id> \
--policy-name default \
--output text \
--query 'Policy' | jq '.Statement[] | select(.Principal == "*" or .Principal.AWS == "*")'
If the output is empty, your key policy no longer has public access.
Additional Resources
- AWS KMS Key Policies
- Determining Access to AWS KMS Keys
- AWS Security Hub KMS Controls
- AWS Config Rule: kms-key-policy-no-public-access
Notes
-
Impact assessment: Before modifying a key policy, identify all services and applications that use the key. Overly restrictive policies can break dependent services.
-
Condition clauses: If you need to allow access from multiple accounts, use a condition clause instead of a wildcard principal:
"Condition": {
"StringEquals": {
"kms:CallerAccount": ["111111111111", "222222222222"]
}
} -
AWS-managed keys: This check applies only to customer-managed keys. AWS-managed keys (with aliases like
aws/s3,aws/ebs) have AWS-controlled policies that cannot be modified. -
Key rotation: While fixing the policy, consider enabling automatic key rotation if not already enabled. This is a security best practice.
-
Audit trail: KMS operations are logged in AWS CloudTrail. Review CloudTrail logs to understand who has been using the key before restricting access.