DEV Community

Cover image for How to Choose a KMS Key Strategy for SaaS Teams
Ilyas Rufai
Ilyas Rufai

Posted on

How to Choose a KMS Key Strategy for SaaS Teams

Your SaaS stores customer files in S3. One AWS Key Management Service (KMS) key encrypts every bucket because "encryption is on." An enterprise customer asks for customer-managed keys (CMKs) and key rotation evidence. You discover twelve services share one key policy with Principal: "*".

KMS is not checkbox encryption. It is access control for ciphertext. SaaS teams must decide what to centralize, what to isolate per tenant, and how to keep key policies readable as the product grows.

In this tutorial, you will learn how to choose a KMS key strategy for SaaS teams: key types, tenancy models, rotation, cross-account patterns, and verification.

Who this is for: SaaS platform engineers, security architects, and DevSecOps engineers on AWS.

Prerequisites:

  • AWS account with S3, RDS, or similar encrypted services
  • Basic understanding of IAM and resource policies

TL;DR

  • Use AWS managed keys for low-friction defaults; customer managed keys (CMKs) when you need policy control, rotation evidence, or customer bring-your-own-key (BYOK).
  • Prefer one CMK per data class (app secrets, customer blobs, backups), not one key for everything, not one key per customer unless contracts require it.
  • Multi-tenant isolation is usually logical (tenant ID in app layer) plus per-environment keys, dedicated keys per tenant only for regulated tiers.
  • Key policies must name specific roles, avoid account-root wildcards.
  • Verify: only intended roles can kms:Decrypt; CloudTrail shows Decrypt attribution.

Why One Default Key Fails for SaaS

Approach Works when Breaks when
Single CMK for all data Early MVP Customer demands key isolation or audit separation
AWS managed keys only Standard tier, no BYOK You cannot customize key policy or cross-account grant
Per-tenant CMK from day one Regulated enterprise SKU Key policy sprawl, cost, and ops load at scale

Key idea: Match key granularity to contract and blast radius, not to idealized zero-trust diagrams on day one.

KMS key tiers from AWS managed to shared CMK to dedicated tenant CMK

Step 1: Pick Key Types

Key type Control Typical SaaS use
AWS managed (aws/s3, aws/rds) AWS owns rotation and policy Non-sensitive dev sandboxes
Customer managed CMK You define key policy, rotation, aliases Production data, customer-facing storage
AWS CloudHSM / External key store Hardware or external HSM Strict compliance, BYOK contracts

Start production on CMKs. Use AWS managed keys only where you accept the AWS-default policy.

Step 2: Choose a Tenancy Model

Model A, Shared platform key (default tier)

One CMK per environment encrypts all tenant data in S3/RDS. Tenant isolation is application-enforced (tenant ID column, prefix, or row-level security).

s3://app-data-prod/tenant-a/*
s3://app-data-prod/tenant-b/*
         ↓
   CMK: alias/app-data-prod
Enter fullscreen mode Exit fullscreen mode

Model B, Key per data class

Separate CMK for:

  • alias/app-customer-content
  • alias/app-internal-secrets
  • alias/app-backups

Blast radius shrinks: backup operator cannot decrypt live customer content if IAM is scoped.

Model C, Dedicated key per enterprise tenant

For customers who contract for CMK isolation or BYOK:

Tenant Acme → CMK alias/tenant-acme-prod + bucket policy binding
Enter fullscreen mode Exit fullscreen mode

Automate provisioning with Terraform; do not hand-craft key policies per customer without a pipeline.

Step 3: Write Minimal Key Policies

CMK policy allowing app role and deny everyone else by default:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowRootAccountAdmin",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::ACCOUNT_ID:root"
      },
      "Action": "kms:*",
      "Resource": "*"
    },
    {
      "Sid": "AllowAppRoleUseKey",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::ACCOUNT_ID:role/app-api-prod"
      },
      "Action": [
        "kms:Encrypt",
        "kms:Decrypt",
        "kms:GenerateDataKey",
        "kms:DescribeKey"
      ],
      "Resource": "*"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Pair with an S3 bucket policy that requires encryption with this CMK:

{
  "Sid": "DenyIncorrectEncryptionHeader",
  "Effect": "Deny",
  "Principal": "*",
  "Action": "s3:PutObject",
  "Resource": "arn:aws:s3:::app-data-prod/*",
  "Condition": {
    "StringNotEquals": {
      "s3:x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:REGION:ACCOUNT_ID:key/KEY_ID"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 4, Plan Rotation and Grants

  • Enable automatic annual rotation on CMKs where supported.
  • Document manual rotation for keys that cannot auto-rotate (asymmetric use cases).
  • Prefer IAM role access over long-lived KMS grants; use grants only for cross-account microservice patterns with expiry.
aws kms enable-key-rotation --key-id alias/app-customer-content
Enter fullscreen mode Exit fullscreen mode

Step 5, Cross-Account and BYOK Patterns

Scenario Pattern
Central logging account CMK in log account; S3 bucket policy + key policy trust logging role
Customer owns key in their account Cross-account key policy + IAM role in your account with kms:Decrypt on their CMK
Disaster recovery copy Re-encrypt to DR region CMK during replication; do not copy ciphertext without a key strategy

How to Verify This Works

  1. Positive: app role reads encrypted S3 object successfully.
  2. Negative: developer role without key policy gets AccessDenied on kms:Decrypt.
  3. Wrong key test: upload object with a different CMK; bucket policy denies.
  4. CloudTrail: Decrypt events show role ARN, not scattered IAM users.
aws kms describe-key --key-id alias/app-customer-content \
  --query 'KeyMetadata.{Enabled:Enabled,Rotation:KeyRotationEnabled}'
Enter fullscreen mode Exit fullscreen mode

When This Breaks Down

  1. Key policy size limits: too many tenant principals on one policy, move to Model C automation or ABAC patterns.
  2. Lambda cold start + KMS latency: excessive decrypt calls; use data key caching carefully within security review.
  3. Terraform state contains key ARNs only: good; never plaintext data keys in state.
  4. Regulatory checkbox without tenant isolation: shared key with "encrypted" label fails customer security review.

Conclusion

In this tutorial, you learned how to choose a KMS strategy for SaaS: key types, shared vs per-class vs per-tenant CMKs, minimal policies, rotation, and verification.

Pick Model B for your next production environment, enable rotation, and run one negative decrypt test this sprint.

References

Top comments (0)