Back to blog
AWSBest PracticesCloud SecurityIdentity & Access

Secrets Manager Using the AWS-Managed Key Instead of a CMK

Learn why Secrets Manager secrets on the default AWS-managed key are risky, and how to re-encrypt them with a customer-managed KMS key (CMK) plus CI guardrails.

TL;DR

This check flags Secrets Manager secrets encrypted with the default aws/secretsmanager managed key instead of a customer-managed KMS key (CMK). Switching to a CMK gives you control over key policies, rotation, and cross-account access. Fix it by creating a CMK and re-encrypting the secret with --kms-key-id.

Secrets Manager encrypts every secret at rest, so on the surface there is nothing to worry about. The catch is which key does the encrypting. If you never specify one, AWS quietly uses the account default managed key called aws/secretsmanager. That key works, but you do not own its policy, you cannot scope access to it, and you cannot enforce rotation or audit on it the way you can with a customer-managed key.

The Lensix check secretsmanager_nocmk looks at every secret in your account and reports the ones still riding on the AWS-managed key. For database credentials, API tokens, and signing keys, that extra layer of control is usually worth having.


What this check detects

The check inspects the KmsKeyId attribute on each Secrets Manager secret. When you create a secret without specifying a key, that attribute is empty and AWS falls back to the default managed key. Lensix flags any secret in that state.

You can see the same thing yourself with the CLI:

aws secretsmanager describe-secret \
  --secret-id prod/db/credentials \
  --query 'KmsKeyId'

If the result is null or returns the ARN of alias/aws/secretsmanager, the secret is using the managed key and will be flagged.

Note: A CMK (customer-managed key) is a KMS key you create and govern. An AWS-managed key is created automatically on your behalf, named with the aws/ prefix, and its key policy is fixed by AWS. You cannot edit the policy, schedule it for deletion, or disable it.


Why it matters

Both key types encrypt your data with AES-256, so the difference is not about cryptographic strength. It is about control, visibility, and blast radius.

You cannot restrict access to the managed key

With the AWS-managed key, decryption permission is effectively granted to any principal in the account that already has secretsmanager:GetSecretValue. There is no separate KMS gate. With a CMK, you write the key policy, so you can require that a principal hold both the Secrets Manager permission and an explicit kms:Decrypt grant on the key. That second lock is what stops an over-broad IAM policy from quietly handing out your production secrets.

No control over rotation or lifecycle

AWS-managed keys rotate on AWS's schedule and you cannot change it. With a CMK you choose annual automatic rotation, manual rotation, or your own cadence to match a compliance requirement.

Cross-account sharing is impossible

If you ever need to share a secret with another account, for example a centralized credentials account read by workloads in other accounts, you must use a CMK. The managed key policy cannot grant access to external principals, so the share simply will not work.

Warning: Auditing is also weaker with the managed key. CloudTrail records Decrypt events against your CMK with a clear key identity, which makes it far easier to answer "who read this secret and when" during an incident. The managed key muddies that trail because it is shared across every secret in the account.

A concrete attack scenario

An engineer attaches a wildcard policy to a CI role: secretsmanager:GetSecretValue on *. With the managed key, that role can now read every secret in the account, including the production database master password. With CMKs scoped per environment, the same role only decrypts secrets whose key policy explicitly allows it. The CMK becomes a second, independent boundary that the wildcard IAM policy never touches.


How to fix it

The fix has two parts: create a CMK, then point the secret at it.

Step 1: Create a customer-managed key

aws kms create-key \
  --description "Secrets Manager - prod" \
  --tags TagKey=purpose,TagValue=secretsmanager

# create a friendly alias for it
aws kms create-alias \
  --alias-name alias/secrets-prod \
  --target-key-id <key-id-from-previous-output>

Step 2: Attach a key policy that scopes decryption

This policy lets account admins manage the key and lets Secrets Manager use it, while limiting Decrypt to a specific role.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAdmins",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:root" },
      "Action": "kms:*",
      "Resource": "*"
    },
    {
      "Sid": "AllowSecretsManagerUse",
      "Effect": "Allow",
      "Principal": { "Service": "secretsmanager.amazonaws.com" },
      "Action": [
        "kms:Encrypt",
        "kms:Decrypt",
        "kms:GenerateDataKey",
        "kms:CreateGrant"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowAppRoleDecrypt",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:role/prod-app" },
      "Action": "kms:Decrypt",
      "Resource": "*"
    }
  ]
}

Step 3: Re-encrypt the secret with the CMK

Danger: Updating --kms-key-id re-encrypts the current secret version. Make sure the new key policy already grants secretsmanager.amazonaws.com usage before you run this, otherwise applications that read the secret may start failing with access-denied errors on decrypt.

aws secretsmanager update-secret \
  --secret-id prod/db/credentials \
  --kms-key-id alias/secrets-prod

Verify the change took effect:

aws secretsmanager describe-secret \
  --secret-id prod/db/credentials \
  --query 'KmsKeyId'

Warning: A dedicated CMK costs roughly $1 per month plus usage-based request charges. That is trivial for a production secret, but if you have thousands of low-value secrets, group them under a few shared CMKs by environment rather than one key per secret.

Fixing it in Terraform

If your secrets live in infrastructure as code, encode the key directly so it never drifts back:

resource "aws_kms_key" "secrets" {
  description             = "Secrets Manager - prod"
  enable_key_rotation     = true
  deletion_window_in_days = 30
}

resource "aws_kms_alias" "secrets" {
  name          = "alias/secrets-prod"
  target_key_id = aws_kms_key.secrets.key_id
}

resource "aws_secretsmanager_secret" "db" {
  name       = "prod/db/credentials"
  kms_key_id = aws_kms_key.secrets.arn
}

Tip: Set enable_key_rotation = true on the key resource so AWS rotates the underlying key material yearly with no application changes. The CMK ARN stays the same, so nothing downstream breaks.


How to prevent it from happening again

One-off fixes do not stick. New secrets get created by developers, pipelines, and RDS automation, and any of them can default back to the managed key. Catch it before it ships.

Block it in CI with a policy check

For Terraform, a Checkov rule flags secrets without a CMK at plan time. It maps to CKV_AWS_149:

checkov -d . --check CKV_AWS_149

For OPA / Conftest, a Rego policy over the Terraform plan works well:

package main

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_secretsmanager_secret"
  not resource.change.after.kms_key_id
  msg := sprintf("Secret '%s' must specify a customer-managed kms_key_id", [resource.address])
}

Catch existing drift continuously

Policy-as-code only covers resources that flow through your pipeline. Click-ops secrets and RDS-managed secrets slip past it. Run the Lensix secretsmanager_nocmk check on a schedule so anything created outside IaC still gets flagged, and wire the finding into your alerting so it does not sit in a dashboard nobody reads.

Tip: RDS, Redshift, and other managed services that auto-create Secrets Manager secrets will use the managed key by default. When you let those services manage credentials, pass a CMK in the service configuration so the generated secret inherits it.


Best practices

  • Use one CMK per environment, not per secret. A secrets-prod and secrets-staging key keeps blast radius separated without exploding your key count or cost.
  • Scope the key policy tightly. The value of a CMK comes from the kms:Decrypt boundary. Grant it only to the roles that genuinely need to read those secrets.
  • Enable automatic key rotation. It is one flag and removes a recurring manual task.
  • Set a deletion window of 30 days. Scheduled deletion is reversible during the window, which protects you from accidentally bricking every secret tied to the key.
  • Monitor kms:Decrypt in CloudTrail. Per-key visibility is half the reason to use a CMK, so actually use it. Alert on decrypts from unexpected principals.
  • Combine KMS controls with least-privilege IAM. Neither layer is sufficient alone. The CMK policy is your safety net for when an IAM policy is too broad.

The managed key is fine until the day someone attaches a wildcard policy or you need to share a secret across accounts. A CMK costs a dollar a month and turns both of those problems into non-events.

Run the check, identify which secrets are still on the default key, and migrate the sensitive ones first. Production database and signing-key secrets are the place to start.