Skip to main content

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:GetKeyPolicy and kms:PutKeyPolicy

Test your access:

aws kms list-keys --region us-east-1 --query 'Keys[0].KeyId'

AWS Console Method

  1. 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)
  2. 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)
  3. 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
  4. Identify the problem

    • Look for policy statements where:
      • "Principal" is set to "*" or {"AWS": "*"}
      • "Effect" is "Allow"
      • There is no "Condition" block restricting access
  5. 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:

  1. In the AWS Console:

    • Return to the KMS key details page
    • Check the Key policy tab
    • Confirm no statements have "Principal": "*" without conditions
  2. 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

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.