Encryption at Rest means encrypting data when it’s stored on disk (at rest), as opposed to data in transit (being transmitted over network).
Why it matters:
We implemented encryption for:
All DynamoDB tables now use AWS KMS encryption:
CloudFormation Template Changes:
ArticlesTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "articles-${Environment}"
# ... other properties ...
# ✅ ADDED: KMS Encryption
SSESpecification:
SSEEnabled: true
SSEType: KMS

1. Application writes data to DynamoDB
↓
2. DynamoDB encrypts data using KMS key
↓
3. Encrypted data stored on disk
↓
4. When reading, DynamoDB decrypts automatically
↓
5. Application receives decrypted data
Key Points:
✅ Security:
✅ Ease of Use:
✅ Performance:
DynamoDB KMS Encryption:
Both S3 buckets now use AES-256 encryption:
CloudFormation Template Changes:
ArticleImagesBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "travel-guide-images-${Environment}-${AWS::AccountId}"
# ... other properties ...
# ✅ ADDED: AES-256 Encryption
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
BucketKeyEnabled: true

1. Application uploads file to S3
↓
2. S3 encrypts file using AES-256
↓
3. Encrypted file stored on disk
↓
4. When downloading, S3 decrypts automatically
↓
5. Application receives decrypted file
Key Points:
✅ Security:
✅ Cost:
✅ Simplicity:
File: travel-guide-backend/core-infra/template.yaml
Changes made:
SSESpecification to all DynamoDB tablesBucketEncryption to all S3 bucketscd travel-guide-backend
# Deploy core infrastructure
./scripts/deploy-core.sh staging
# Or manually:
sam build -t core-infra/template.yaml
sam deploy \
--stack-name travel-guide-core-staging \
--parameter-overrides Environment=staging \
--capabilities CAPABILITY_IAM
Verify DynamoDB:
# Check table encryption
aws dynamodb describe-table \
--table-name articles-staging \
--query 'Table.SSEDescription'
# Expected output:
{
"Status": "ENABLED",
"SSEType": "KMS"
}
Verify S3:
# Check bucket encryption
aws s3api get-bucket-encryption \
--bucket travel-guide-images-staging-123456789012
# Expected output:
{
"ServerSideEncryptionConfiguration": {
"Rules": [
{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
},
"BucketKeyEnabled": true
}
]
}
}
# Create test article
curl -X POST https://api.example.com/articles \
-H "Authorization: Bearer $TOKEN" \
-d '{
"title": "Test Encryption",
"content": "Sensitive data",
"latitude": 10.8231,
"longitude": 106.6297
}'
# Verify data is encrypted in DynamoDB
# (You cannot see encrypted data directly - it's transparent)
# Read article back
curl https://api.example.com/articles/{article-id} \
-H "Authorization: Bearer $TOKEN"
# Data should be readable (decrypted automatically)
# Upload test image
aws s3 cp test-image.jpg \
s3://travel-guide-images-staging-123456789012/test/
# Check object encryption
aws s3api head-object \
--bucket travel-guide-images-staging-123456789012 \
--key test/test-image.jpg \
--query 'ServerSideEncryption'
# Expected: "AES256"
✅ GDPR (EU)
✅ HIPAA (Healthcare)
✅ PCI-DSS (Payment)
✅ SOC 2
DynamoDB:
S3:
Enable CloudTrail to audit:
# Check CloudTrail logs
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=ResourceType,AttributeValue=AWS::KMS::Key \
--max-results 10
✅ Recommended: AWS-managed KMS keys


❌ Avoid: Customer-managed keys (unless required)
✅ Do: Enable at bucket/table level
❌ Don’t: Rely on object-level encryption
✅ Do: Set up CloudWatch alarms
✅ Do: Document which keys encrypt what
Error:
AccessDeniedException: User is not authorized to perform: kms:Decrypt
Solution:
kms:Decrypt and kms:DescribeKey{
"Effect": "Allow",
"Action": [
"kms:Decrypt",
"kms:DescribeKey"
],
"Resource": "arn:aws:kms:*:*:key/*"
}
Problem: Old objects not encrypted
Solution:
# Copy object to itself (re-encrypts)
aws s3 cp \
s3://bucket/key \
s3://bucket/key \
--metadata-directive REPLACE
Problem: Slight latency increase
Solution:
BucketKeyEnabled: trueReduce costs:
No cost: