Encrypting databases at rest: the basics
Why every managed database engine has its own version of the same finding
Every managed database on AWS stores its data on disk, and "at rest" encryption scrambles those bytes with an AWS KMS key so a stolen snapshot, a mis-shared backup or a compromised volume reveals ciphertext instead of customer records. Each engine expresses the same property in its own shape: RDS.3 and RDS.4 cover RDS instances and their snapshots, DocumentDB.1 covers DocumentDB clusters, Neptune.1 and Neptune.6 cover Neptune clusters and snapshots, Redshift.10 covers Redshift clusters, DynamoDB.3 covers DynamoDB Accelerator (DAX) cluster storage, ElastiCache.4 covers replication-group storage, and Opensearch.1 and Opensearch.3 cover OpenSearch domain storage and node-to-node traffic. Different APIs, identical risk: a data store readable on disk.
AWS Security Hub turns each engine into its own control, so a single estate can fail ten or more at-rest encryption checks at once. They look like separate problems on the report, but they are one capability: prove that the bytes on disk for every database, and every snapshot derived from it, are encrypted under a key you can govern. The bytes themselves are protected with AES-256; the value is in the KMS key boundary, which lets you authorise, log and revoke access to the data without touching the database itself.
The trait that makes this capability sharp is that, for most of these engines, encryption is chosen at creation and cannot be toggled on afterwards. RDS, DocumentDB and Neptune all bind encryption to the storage allocation, so there is no modify flag to flip. The supported path is to snapshot, copy the snapshot with a KMS key (the copy is where encryption is applied), restore a new encrypted cluster and cut over. A few engines are more forgiving: Redshift runs a background migration via modify-cluster, and OpenSearch lets you enable encryption at rest on an existing domain (OpenSearch or Elasticsearch 6.7 or later, with no UltraWarm or cold storage and on a supported instance type). Either way, the cost of fixing it only grows as the database accumulates data, which is the whole argument for catching it early.
In this lesson you will learn how AWS expresses encryption at rest across its database engines, why most of them make encryption immutable at creation time, the snapshot-copy-restore migration that is the only supported fix for the immutable engines (and the in-place paths that Redshift and OpenSearch offer instead), and how to stop new unencrypted databases from ever appearing. The Controls this lesson covers section lists every Security Hub control in this capability, each linking to a deep page with the exact check and a copy-and-paste fix.
The setting you can never change your mind on
For most AWS database engines, at-rest encryption is one of the very few settings that is genuinely immutable for the life of the resource. You choose it at create time and that is it: there is no modify-db-instance flag for RDS storage encryption, and no in-place enable for DocumentDB or Neptune. The architectural reason is that encryption is applied at the storage layer beneath the engine, so flipping it would mean rewriting every block in place, which those services simply do not offer. The only way across the boundary is to snapshot, copy the snapshot with the KMS key you want, and restore a brand-new resource from the encrypted copy. A couple of engines are kinder: Redshift's modify-cluster runs a background migration for you (still a migration, not a toggle), and OpenSearch actually does let you enable encryption at rest on an existing domain, provided it runs OpenSearch or Elasticsearch 6.7 or later, has no UltraWarm or cold storage, and uses a supported instance type.
Finding unencrypted databases across an estate
Priya is the platform lead at a scale-up preparing for its first SOC 2 audit. Security Hub shows at-rest encryption failures spread across RDS, Redshift and OpenSearch in three accounts that pre-date the team's current encryption standard.
Rather than work the findings engine by engine, she starts by listing what is actually unencrypted across the database fleet, so she can separate the regulated-data stores that need migrating first from the throwaway ones that can simply be deleted.
Start with RDS, the most common and highest-impact failure. The StorageEncrypted field is exactly what RDS.3 evaluates.
Inventory first: prioritise the regulated-data stores, not the order the report lists them.
How AWS encrypts database storagedeep dive
Across these engines, encryption at rest is an AES-256 envelope scheme keyed by KMS, applied at the storage layer beneath the database. A per-resource data key encrypts the data blocks, and that data key is itself wrapped by a KMS key, either the AWS-managed key for the service (such as aws/rds or aws/es) or a customer-managed key you specify. Only the bottom keys ever touch the data; the upper tiers exist so that key rotation and access revocation happen without re-encrypting terabytes of blocks. Revoke access to the KMS key and the whole chain becomes unusable, which is the access-management property the controls are really about.
Snapshots inherit the encryption state of their source. An unencrypted RDS, Redshift or Neptune instance produces unencrypted snapshots, and those snapshots can be copied cross-region or shared cross-account, so plaintext copies propagate far from the original and persist long after the source is gone. Encrypting the source going forward does not retroactively encrypt snapshots already taken, which is why RDS.4, Neptune.6 and the snapshot-sharing controls are tracked separately: you have to find and re-encrypt or delete the legacy snapshots as their own step.
Security Hub evaluates these through AWS Config rules such as rds-storage-encrypted, redshift-cluster-kms-enabled and elasticsearch-encrypted-at-rest. Some are change-triggered (they clear shortly after a fixed resource registers) and some are periodic (they clear on the next scheduled evaluation, not instantly), which matters when you are gathering audit evidence against a deadline. Two engines deserve a note: DynamoDB tables are always encrypted, so DynamoDB.3 is not about tables at all but about DynamoDB Accelerator (DAX) clusters, which must be encrypted at rest and whose encryption cannot be changed after creation; and Opensearch.3 covers node-to-node encryption, the in-cluster traffic between data nodes, which is distinct from on-disk storage but lives in the same hardening pass.
What is the impact of leaving a database unencrypted?
The direct impact is data exposure. An unencrypted database stores customer records, credentials, financial data and PII as plaintext on the underlying disks and in every backup and snapshot it produces. If any of those artefacts leaks (a snapshot shared to the wrong account, a backup copied to an unsecured bucket, a storage-layer compromise) the data is immediately readable. Encryption at rest is the control that turns that worst case from a reportable breach into a non-event, because the leaked copy is ciphertext without the KMS key.
The compliance impact is where these controls earn their weight. Encryption of data at rest is an explicit requirement in PCI DSS, a near-universal expectation in SOC 2 reports, and a control under HIPAA, ISO 27001 and most NIST-based regimes (SC-13 and SC-28). A single unencrypted production database holding regulated data is the kind of finding an auditor circles, that delays a certification, or that has to be disclosed in a vendor security questionnaire. The Medium severity that Security Hub assigns most of these controls understates the regulatory consequence.
The structural impact is the immutability constraint itself. Because most engines cannot toggle encryption on later, every unencrypted database is a standing liability that grows more painful to fix as it accumulates data and dependencies. A 20 GB database is a quick migration; a 4 TB cluster with tight uptime is a planned project. The cost of inaction is not a monthly dollar figure: it is the compounding difficulty of the eventual, unavoidable migration, plus every unencrypted snapshot that piles up in the meantime.
How do you encrypt the database estate safely?
Work the capability as one loop rather than chasing individual engine findings. The order matters: inventory and prioritise by data sensitivity, migrate the immutable engines via snapshot-copy-restore (or use the in-place path where Redshift and OpenSearch offer one), clean up legacy unencrypted snapshots, then add a preventive guardrail so the gap cannot reopen.
1. Inventory every database and prioritise by data sensitivity
List every RDS, Aurora, DocumentDB, Neptune, Redshift, DynamoDB, ElastiCache and OpenSearch resource with its encryption flag, engine, size and the classification of what it holds. Do not treat all findings equally: an unencrypted throwaway dev database may simply be deleted, while a production database with customer records is the urgent one. Sort by data sensitivity and uptime sensitivity so the migration effort goes where the exposure actually is.
2. Migrate the immutable engines: snapshot, copy with a KMS key, restore
For RDS, DocumentDB and Neptune there is no in-place toggle. Take a snapshot of the source (it is unencrypted), copy the snapshot passing a KMS key (the copy is where encryption is applied), then restore a new resource from the encrypted copy. Reapply the original parameter group, security groups and subnet group. For large databases, do the bulk restore ahead of time and schedule a short maintenance window for the final delta and the endpoint cutover. Only delete the old unencrypted resource after verifying the new one is healthy.
3. Use the in-place path where the engine offers one
Redshift takes an in-place path: modify-cluster with --encrypted runs a background migration that provisions a new encrypted cluster and copies data across. During that migration the cluster status shows resizing and it is available in read-only mode (RA3 node types return to read/write quickly while restored blocks re-encrypt in the background), so plan it in a low-traffic window. OpenSearch also lets you enable encryption at rest on an existing domain (OpenSearch or Elasticsearch 6.7 or later, no UltraWarm or cold storage, supported instance type), and Opensearch.3 node-to-node encryption is likewise a configuration change. Two engines do not have an in-place at-rest path: DynamoDB.3 is about DAX clusters, whose at-rest encryption cannot be changed after creation, so you must recreate the cluster; and ElastiCache.4 requires creating a new replication group by restoring from a backup with encryption enabled, then cutting over, because at-rest encryption cannot be toggled on an existing replication group. Match the path to the engine.
4. Clean up legacy snapshots and enforce encryption by default
Encrypting a cluster does not encrypt snapshots taken before the change, so find and delete or re-encrypt the legacy unencrypted snapshots before they are shared or copied. Then prevent recurrence: enable encryption-by-default where the engine supports it, and back it with a Service Control Policy or AWS Config rules (such as rds-storage-encrypted) that flag or deny creation of an unencrypted database. That converts these controls from a recurring cleanup chore into a one-time migration followed by a prevention guardrail.
# 1. Find unencrypted databases across engines (sample: RDS, Redshift, OpenSearch).
aws rds describe-db-instances \
--query 'DBInstances[?StorageEncrypted==`false`].DBInstanceIdentifier' --output text
aws redshift describe-clusters \
--query 'Clusters[?Encrypted==`false`].ClusterIdentifier' --output text
# 2. Immutable engine (RDS): snapshot, copy WITH a KMS key, restore the new instance.
SRC=prod-orders-db; KEY=alias/db-encryption
aws rds create-db-snapshot --db-instance-identifier $SRC --db-snapshot-identifier ${SRC}-snap
aws rds wait db-snapshot-completed --db-snapshot-identifier ${SRC}-snap
aws rds copy-db-snapshot \
--source-db-snapshot-identifier ${SRC}-snap \
--target-db-snapshot-identifier ${SRC}-snap-enc \
--kms-key-id $KEY
aws rds wait db-snapshot-completed --db-snapshot-identifier ${SRC}-snap-enc
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier ${SRC}-enc --db-snapshot-identifier ${SRC}-snap-enc
# 3. In-place engine (Redshift): background migration (cluster is read-only during it).
aws redshift modify-cluster --cluster-identifier analytics-prod \
--encrypted --kms-key-id $KEY
# 4. Prevent recurrence: deploy the rds-storage-encrypted Config rule to flag any
# new unencrypted RDS instance (back it with an SCP to deny creation outright).
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "rds-storage-encrypted",
"Source": {"Owner": "AWS", "SourceIdentifier": "RDS_STORAGE_ENCRYPTED"}
}' Quick quiz
Question 1 of 5Security Hub shows at-rest encryption failures across RDS, DocumentDB, Neptune and OpenSearch. What is the most efficient way to think about them?
You scored
0 / 5
Keep learning
Go deeper on how at-rest encryption works across the database engines in this capability.
- Encrypting Amazon RDS resources How RDS encryption at rest works, the immutability constraint, and the snapshot-copy-restore migration path.
- Amazon Redshift database encryption The KMS key hierarchy and the in-place migration that enabling encryption on an existing cluster runs.
- Encrypting Amazon DocumentDB data at rest Why DocumentDB encryption is set at creation, and how to satisfy DocumentDB.1 via the snapshot-copy path.
You can now treat database encryption at rest as one capability rather than a scatter of engine-specific findings: inventory every store, prioritise the regulated-data ones, migrate the immutable engines via snapshot-copy-restore (using the in-place path where Redshift and OpenSearch offer one, and recreating a DAX cluster for DynamoDB.3), clean up legacy unencrypted snapshots, and add a preventive guardrail so the gap cannot reopen. The Controls this lesson covers section below links every control in this group to its deep page and fix.
Back to the library