Skip to main content
emnode / learn
Compliance Medium severity

AWS Security Hub · RDS

RDS.39: RDS MySQL should be encrypted in transit

Written and reviewed by Emnode · Last reviewed

What does AWS Security Hub RDS.39 check?

RDS.39 evaluates the AWS::RDS::DBInstance resource on a periodic schedule and checks the rds.require_secure_transport parameter on a standalone RDS for MySQL instance. It reports FAILED when the parameter is 0 (off). It does not evaluate instances that are members of a DB cluster.

Why does RDS.39 matter?

Every RDS for MySQL instance ships with a live TLS endpoint, but a fresh instance only offers TLS — it does not require it. A client connecting with --ssl-mode=DISABLED, or an older driver omitting the flag, negotiates a cleartext session the server accepts, and every query, result row, and bind parameter crosses the network in plaintext, visible to anything with packet capture on the path. Setting the parameter to 1 makes the server reject any non-TLS handshake with error 3159.

How do I fix RDS.39?

  1. Confirm all clients and drivers support TLS.
  2. Set rds.require_secure_transport=1 in a custom DB parameter group attached to the instance.
  3. Reboot if required, then verify cleartext connections are rejected.
  4. Standardise the parameter group across standalone MySQL instances.

Remediation script · bash

# Find the highest-impact plaintext-permitting stores across engines.
aws rds describe-db-instances \
  --query 'DBInstances[].DBInstanceIdentifier' --output text
aws elasticache describe-replication-groups \
  --query 'ReplicationGroups[?TransitEncryptionEnabled==`false`].ReplicationGroupId' \
  --output text

# RDS for PostgreSQL: require TLS via rds.force_ssl (static -> needs a reboot).
PG=$(aws rds describe-db-instances --db-instance-identifier prod-orders-pg \
  --query 'DBInstances[].DBParameterGroups[].DBParameterGroupName' --output text)
aws rds modify-db-parameter-group --db-parameter-group-name "$PG" \
  --parameters 'ParameterName=rds.force_ssl,ParameterValue=1,ApplyMethod=pending-reboot'
aws rds reboot-db-instance --db-instance-identifier prod-orders-pg

# Redshift: require_ssl on a custom cluster parameter group, then reboot.
aws redshift modify-cluster-parameter-group --parameter-group-name analytics-tls \
  --parameters ParameterName=require_ssl,ParameterValue=true
aws redshift reboot-cluster --cluster-identifier analytics-prod

Full walkthrough (console steps, edge cases and verification) in the lesson Enforce TLS on database and cache connections.

Is RDS.39 a false positive?

RDS.39 runs on a periodic schedule, not change-triggered — so flipping the parameter does not move the finding to PASSED immediately; the evaluator re-reads the effective parameter set on its own cadence. It also targets standalone MySQL only, not Aurora MySQL cluster members.

Part of the learning path Encrypt everything
  • RDS.1 An RDS snapshot is shared publicly
  • RDS.2 An RDS instance is publicly accessible from the internet
  • RDS.3 RDS DB instances should be encrypted at rest
  • RDS.4 RDS snapshots should be encrypted at rest
  • RDS.5 RDS DB instances should use multiple AZs
  • RDS.6 RDS lacks enhanced monitoring
  • RDS.7 RDS clusters should have deletion protection
  • RDS.8 RDS DB instances should have deletion protection
  • RDS.9 RDS engine logs are not shipped to CloudWatch
  • RDS.10 RDS relies on long-lived database passwords
  • RDS.11 RDS instances should have automatic backups
  • RDS.12 IAM auth should be configured for RDS clusters