Encryption
TSC mapping: CC6.1 (Logical access security — data at rest), CC6.7 (Data transmission and removal controls)
Auditors require evidence that customer data is encrypted at rest and in transit, that encryption keys are managed with proper access controls, and that secrets are not stored in plaintext.
1. AWS KMS — Key Management
Customer-managed KMS keys (CMKs) give you full control over key policies, rotation, and audit trails. Use CMKs instead of AWS-managed keys for any data requiring strong access segregation.
Create a CMK with automatic rotation
# Create a symmetric CMK
aws kms create-key \
--description "SOC2 data encryption key" \
--key-usage ENCRYPT_DECRYPT \
--tags TagKey=compliance,TagValue=soc2
# Enable automatic annual rotation
aws kms enable-key-rotation --key-id <key-id>
# Verify rotation is enabled
aws kms get-key-rotation-status --key-id <key-id>
Restrict key access with a key policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowKeyAdmins",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<account>:role/KeyAdminRole"
},
"Action": [
"kms:Create*", "kms:Describe*", "kms:Enable*",
"kms:List*", "kms:Put*", "kms:Update*", "kms:Revoke*",
"kms:Disable*", "kms:Get*", "kms:Delete*", "kms:ScheduleKeyDeletion",
"kms:CancelKeyDeletion"
],
"Resource": "*"
},
{
"Sid": "AllowKeyUsage",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<account>:role/AppRole"
},
"Action": [
"kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*",
"kms:GenerateDataKey*", "kms:DescribeKey"
],
"Resource": "*"
},
{
"Sid": "DenyKeyDeletion",
"Effect": "Deny",
"Principal": "*",
"Action": ["kms:ScheduleKeyDeletion", "kms:DeleteImportedKeyMaterial"],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalArn": "arn:aws:iam::<account>:role/KeyAdminRole"
}
}
}
]
}
AWS Config rule:
| Rule | What it checks |
|---|---|
kms-cmk-not-scheduled-for-deletion | No CMK is pending deletion |
cmk-backing-key-rotation-enabled | CMK automatic rotation is enabled |
Reference: AWS KMS documentation →
2. S3 — Encryption at Rest
Enable default bucket encryption (SSE-KMS)
aws s3api put-bucket-encryption \
--bucket my-sensitive-bucket \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:<region>:<account>:key/<key-id>"
},
"BucketKeyEnabled": true
}]
}'
Block all public access at the account level
aws s3control put-public-access-block \
--account-id <account-id> \
--public-access-block-configuration \
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
Enforce HTTPS-only access
aws s3api put-bucket-policy \
--bucket my-sensitive-bucket \
--policy '{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenyHTTP",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::my-sensitive-bucket",
"arn:aws:s3:::my-sensitive-bucket/*"
],
"Condition": {
"Bool": {"aws:SecureTransport": "false"}
}
}]
}'
Enable S3 access logging
aws s3api put-bucket-logging \
--bucket my-sensitive-bucket \
--bucket-logging-status '{
"LoggingEnabled": {
"TargetBucket": "my-access-logs-bucket",
"TargetPrefix": "s3-access-logs/my-sensitive-bucket/"
}
}'
AWS Config rules:
| Rule | What it checks |
|---|---|
s3-bucket-server-side-encryption-enabled | Default encryption on all buckets |
s3-bucket-public-access-prohibited | Account-level public access block |
s3-bucket-logging-enabled | Access logging enabled |
s3-bucket-ssl-requests-only | Bucket policy denies HTTP |
Reference: S3 server-side encryption →
3. EBS — Encryption at Rest
Enable default EBS encryption in every region so all new volumes are encrypted automatically, regardless of how they are created.
# Enable per region (run for every region in use)
aws ec2 enable-ebs-encryption-by-default --region us-east-1
aws ec2 enable-ebs-encryption-by-default --region eu-west-1
# Verify
aws ec2 get-ebs-encryption-by-default --region us-east-1
Verify existing volumes:
aws ec2 describe-volumes \
--query 'Volumes[?Encrypted==`false`].[VolumeId,AvailabilityZone,Size]' \
--output table
AWS Config rule:
| Rule | What it checks |
|---|---|
ec2-ebs-encryption-by-default | Default EBS encryption is enabled per region |
encrypted-volumes | All attached EBS volumes are encrypted |
4. RDS — Encryption at Rest
RDS encryption must be enabled at creation time — it cannot be added to an existing unencrypted instance.
# Create an encrypted RDS instance
aws rds create-db-instance \
--db-instance-identifier prod-db \
--db-instance-class db.t3.medium \
--engine postgres \
--engine-version "15.4" \
--master-username admin \
--master-user-password <from-secrets-manager> \
--storage-encrypted \
--kms-key-id arn:aws:kms:<region>:<account>:key/<key-id> \
--backup-retention-period 7 \
--deletion-protection \
--no-publicly-accessible
Audit unencrypted RDS instances:
aws rds describe-db-instances \
--query 'DBInstances[?StorageEncrypted==`false`].[DBInstanceIdentifier,DBInstanceClass,Engine]' \
--output table
AWS Config rules:
| Rule | What it checks |
|---|---|
rds-storage-encrypted | All RDS instances have storage encryption enabled |
rds-instance-public-access-check | No RDS instance is publicly accessible |
rds-automatic-minor-version-upgrade-enabled | Auto minor version upgrades are on |
rds-backup-enabled | Automated backups enabled with retention ≥ 7 days |
Reference: RDS encryption at rest →
5. AWS Secrets Manager — Secret Storage and Rotation
Store all credentials, API keys, and connection strings in Secrets Manager. Never store them in environment variables, code, or SSM Parameter Store as plain text.
# Store a database credential
aws secretsmanager create-secret \
--name prod/app/database \
--description "Production database credentials" \
--kms-key-id arn:aws:kms:<region>:<account>:key/<key-id> \
--secret-string '{"username":"app_user","password":"<password>","host":"prod-db.cluster.region.rds.amazonaws.com","port":5432}'
# Enable automatic rotation (e.g., every 30 days for RDS)
aws secretsmanager rotate-secret \
--secret-id prod/app/database \
--rotation-lambda-arn arn:aws:lambda:<region>:<account>:function:SecretsManagerRDSRotation \
--rotation-rules AutomaticallyAfterDays=30
Retrieve a secret in application code (do not log the value):
aws secretsmanager get-secret-value \
--secret-id prod/app/database \
--query SecretString \
--output text
AWS Config rule:
| Rule | What it checks |
|---|---|
secretsmanager-rotation-enabled-check | All secrets have rotation configured |
secretsmanager-scheduled-rotation-success-check | Last rotation succeeded |
secretsmanager-secret-unused | Secrets not accessed in N days are flagged |
Reference: AWS Secrets Manager documentation →
6. Other Services — Encryption Checklist
| Service | Control | Command to verify |
|---|---|---|
| ElastiCache (Redis) | Enable in-transit and at-rest encryption | aws elasticache describe-replication-groups --query 'ReplicationGroups[*].[ReplicationGroupId,AtRestEncryptionEnabled,TransitEncryptionEnabled]' |
| DynamoDB | Enable table encryption with CMK | aws dynamodb describe-table --table-name <table> --query 'Table.SSEDescription' |
| SQS | Enable SSE-KMS on all queues | aws sqs get-queue-attributes --attribute-names KmsMasterKeyId |
| SNS | Enable server-side encryption | aws sns get-topic-attributes --topic-arn <arn> --query 'Attributes.KmsMasterKeyId' |
| EFS | Enable encryption at rest and in transit | aws efs describe-file-systems --query 'FileSystems[*].[FileSystemId,Encrypted]' |
| Kinesis | Enable SSE on data streams | aws kinesis describe-stream-summary --stream-name <name> |
SOC 2 Evidence for Encryption
| Evidence item | How to collect |
|---|---|
| KMS key list with rotation status | aws kms list-keys + get-key-rotation-status per key |
| S3 bucket encryption configuration | aws s3api get-bucket-encryption --bucket <name> |
| S3 public access block status | aws s3control get-public-access-block --account-id <id> |
| EBS default encryption status | aws ec2 get-ebs-encryption-by-default per region |
| RDS encryption status | aws rds describe-db-instances |
| Secrets Manager rotation status | aws secretsmanager list-secrets |
| Config rule compliance (encryption rules) | AWS Config console → Compliance by rule |