In the first post of the series, I described some generic security concepts and corresponding AWS security controls that can be applied to data stores on AWS. Using these, you can create a stronger security posture around your data. In this second post, I demonstrate how these concepts can be implemented to Amazon RDS databases.
Although many of the implementation examples are common to all RDS database engines, a few might differ based on the individual engine type. In these cases, I include implementation examples from Amazon Aurora with MySQL compatibility but also point you to where to get the information for other database engines.
Let’s walk through the implementation of the security concepts in the order in which they were described in the first post.
Data classification and security-zone modeling
For a refresher on data classification and security-zone modeling, see following. For a deeper description than provided here and for the concepts behind data classification and security-zone modeling, see the first post of the series.
To decide which of the security controls described later in this post apply to you, understand the classification of your data. For example, you might not need some of the specialized security controls like tokenization of data or security microsegmentation, both described later in this post. A case when you might not need these is if you don’t have any highly sensitive data like credit card numbers or Social Security numbers in your database.
After you design your security zones, implement them using network access control lists (ACLs). We recommend this so that you have the entire subnet as the network flow control barrier. I show how to implement security zones with network ACLs in the Security groups and network ACLs section later in this post.
When implementing security-zone modeling, carefully consider your networking design. The size of your CIDR ranges determines the number of IP addresses each of your subnets can sustain. Design your CIDR ranges so they can support growth within a subnet (more IP addresses) and growth in the number of subnets. Balance this out with any requirements to have nonconflicting IP address space between your Amazon VPC and on-premises data center or between VPCs. To learn more, see AWS Single VPC Design on the AWS Answers site.
Defense in depth
Before I dive into the security controls for defense in depth within RDS, let’s look at the RDS launch wizard sections that cover the security configurations described later in this post.
To get to the security configuration settings, navigate to RDS in the AWS Management Console.
Choose Create Database. For the purposes of this blog post, I work with an Aurora MySQL database instance.
Let’s review what we will modify as part of the RDS security controls described later in the post. Follow the launch wizard until you reach Step 3 – Configure advanced settings.
Modifying other default RDS configurations not related to the security controls covered in this blog post is outside of the scope of this post. To follow a complete step-by-step guide to launching an Aurora database instance, see Getting Started with Amazon Aurora in the RDS documentation. For other RDS database engines, see Getting Started with Amazon RDS.
Now let’s discuss the security controls that make up defense in depth. The diagram following is from the first post, which describes in detail the concept of defense in depth. There are two main categories of controls, preventative controls and detective controls.
You can help protect your application and database from distributed denial of service (DDOS) attacks by working with AWS Shield Standard and Advanced. All AWS customers benefit from the automatic protections of AWS Shield Standard, at no additional charge. AWS Shield Standard defends against most common, frequently occurring network and transport layer that target your web site or applications. When you use AWS Shield Standard with Amazon CloudFront and Amazon Route 53, you receive comprehensive availability protection against all known infrastructure (layer 3 and 4) attacks.
For higher levels of protection including access to a DDOS response team 24/7, you can subscribe to AWS Shield Advanced. For more information on the additional protections available, see the AWS Shield Advanced features page. Following is an example of enabling AWS Shield Advanced.
Navigate to AWS WAF and AWS Shield in the AWS console. If you have not yet enabled AWS WAF or AWS Shield Advanced, then you see the following page. Otherwise, you see the AWS WAF and AWS Shield dashboard page.
Choose AWS Shield to see the comparison of Standard and Advanced. For the purposes of this example, we enable AWS Shield Advanced.
Application-level threat prevention
To help with application-level threat prevention, we recommend AWS WAF and database firewalls, as described following.
Your web application needs to access data stored in your database. Thus, it’s important to protect the data against leakage by using attacks on applications like SQL injection and others of the Open Web Application Security Project (OWASP) top 10 most critical security risks to web applications. (For more information on these, see the OWASP site.) You can configure AWS WAF to protect your application against these threats by enabling it either within your Amazon CloudFront distribution or with Application Load Balancers.
To start work with AWS WAF, navigate to AWS WAF and AWS Shield in the console. If you haven’t yet enabled AWS WAF or AWS Shield Advanced, then you see the following page. Otherwise, you see the AWS WAF and AWS Shield dashboard page.
If this is the first web ACL in your account, you see the following page.
Otherwise, create a new web ACL as follows.
Next, configure the web ACL.
To set up an AWS WAF rule, first create some conditions to use in the detection of application level threats. Following shows an example of creating a SQL injection condition. You can create multiple conditions, each with multiple filters.
Next, create an AWS WAF rule that uses the preceding condition.
An additional approach for protecting against application level attacks is to implement a database firewall solution from an AWS partner. You can find these solutions on the AWS Marketplace.
A key security consideration for your sensitive data is the network isolation of the database. Network isolation makes your database accessible only on a private IP address range to only those components that require access to it. The fundamental design component that enables this security isolation is Amazon VPC. You associate a VPC with your database instance at the time that you create the database instance.
Let’s start with creating a VPC. Navigate to VPC in the AWS Console and choose Create VPC.
Choose a name for your VPC and specify a CIDR range.
Next, we create VPC subnets exclusively for our RDS database. On the VPC Dashboard navigation pane, choose Subnets, then choose Create subnet.
Name your subnet and provide a CIDR range and Availability Zone.
Repeat this step for each of the Availability Zones within your AWS Region. In the case of the AWS Region for this blog example, ap-southeast-2, there are three Availability Zones. Therefore I repeat this step three times to create three subnets, one in each Availability Zone. For more details on working with Availability Zones within AWS Regions, see Regions and Availability Zones in the Amazon EC2 documentation.
The next step is to add the subnets for the database into an Amazon RDS construct called a subnet group, as shown in the examples following. A subnet group is a collection of subnets where your RDS instances are deployed; for more details, see Working with DB Subnet Groups in the RDS documentation.
Navigate to RDS in the AWS console and choose Create DB Subnet Group to create a new subnet group.
Now we associate the VPC and the subnet group that we have created with the database instance and also choose No for public accessibility. You can do this in Network & Security section of the RDS launch wizard.
Security groups and network ACLs
Security groups and network ACLs are also important for security, as I describe following.
Use network ACLs to implement security zone modeling (for details on working with network ACLs, see Network ACLs in the VPC documentation). A security zone provides a well-defined network perimeter that contains assets with a similar trust level. A security zone also enables clarity and ease of reasoning for defining and enforcing network flow control into and out of the security zone based on its characteristics.
Following is a diagram representing one approach to security zone modeling.
In the model in this diagram, the subnets for the database that we created previously logically belong to the Secure Zone. Let’s create the network ACLs to apply the appropriate network traffic control. Because network ACLs are stateless, both the inbound and outbound rules must be specified explicitly. As shown in the examples following, the inbound and outbound rules allow traffic only to or from CIDR ranges that belong to database consumers. An example is the application tier, depicted as the Internal (Private) Zone in the diagram preceding.
Inbound traffic is also restricted to the ports configured for Aurora MySQL (by default its port is 3306). For any other RDS database, you use the port configured for that database.
To create network ACLs for the Secure Zone subnets, navigate to VPC in the console and choose Network ACL.
Note: Follow similar steps for the subnets belonging to the other security zones.
Note: Set up your network ACL outbound rules to allow traffic on the ephemeral port ranges that are used by MySQL clients to communicate to the database. In addition, set up the rules to allow traffic on any additional ports for data replication or administration traffic, like Active Directory traffic for Microsoft SQL Server.
Associate the Secure Zone network ACLs with subnets where the database is deployed, as shown in the example following.
Subnets that make up the Secure Zone are part of a layered security zone model, as shown in the security zone modeling diagram preceding. Create additional security zones to host the application layers that interact with the Secure Zone and also with each other. The application layer security zones provide an additional level of isolation for the Secure Zone by filtering network traffic through their own network ACLs.
Why would you need a security group for your database if you have already defined a network ACL for the subnet of the database? Use security groups to create a layered security design and microsegmentation of databases. For details on working with security groups, see Security Groups for Your VPC in the VPC documentation.
Say that you have two microservices within the application tier—Internal (Private) Zone in the diagram preceding—and two corresponding databases within the database tier (Secure Zone). Each database is associated with the one of the two microservices.
Both of the microservices send database traffic to the Secure Zone through the network ACL. How do you prevent microservice 1 from sending traffic to the database of microservice 2? You can do this by associating individual security groups with each of the two databases and enabling traffic only from the corresponding microservice that owns the database. The diagram following illustrates this microsegmentation.
The following example illustrates this by configuring allowed inbound traffic only from a dedicated security group, which can be the security group of the corresponding microservice. To start, navigate to VPC in the console and choose Security Groups.
To attach this security group when you launch your RDS database instance, scroll to Network & Security. Choose the security group that you have created for your database instance.
AWS Identity and Access Management
AWS Identity and Access Management (IAM) is the fundamental control available to customers building on AWS. It is the first of the five best practices of the Security pillar of the AWS Well Architected Framework. By using the framework, you can learn architectural best practices for designing and operating reliable, secure, efficient, and cost-effective systems in the cloud.
To begin work with IAM, separate access requirements for your control plane operations from those for your data plane operations.
Control plane operations
For RDS, control plane operations refer to management functions on the RDS instance such as CreateDBInstance, ModifyDBInstance, and CreateDBParameterGroup. Because these are higher-privileged operations, apply care and rigor when assigning the related privileges to users or roles.
Following is an example IAM policy that grants a limited set of administrative actions on a specified database instance. You can attach this policy to a user, group, or role.
Data plane operations
Data plane operations typically refer to actions that select, insert, modify, and delete the data in the database. There are two parts to enabling the data plane operations on an Amazon RDS database:
- Database authentication
- Database access control
First is authentication, that is who is allowed to access the database. You can set up authentication within the database. Alternatively, you can set up authentication by using IAM integration if you’re using Amazon Aurora with MySQL or PostgreSQL compatibility or RDS for MySQL or PostgreSQL.
The second part is access control, that is what level of access does a database user have? You define access control within the database.
Database authentication – First, let’s look at how you can set up authentication for data plane operations.
Authentication option 1 is using IAM database authentication. To take this approach, when you launch a new RDS database instance, choose Enable IAM DB authentication.
Connect to the database instance with the master account and create a database account to be integrated with IAM authentication for an IAM user or role:
Add an IAM policy to allow connection to an RDS instance. In this case, select the Aurora cluster as the resource.
Navigate to IAM in the AWS Console and choose Policies.
Modify the policy definition for your environment:
For the Amazon Resource Name (ARN) for this policy, let’s identify the key components that need to be defined. Given the ARN “arn:aws:rds-db:ap-southeast-2:1234567890:dbuser:cluster-WIK2GTMJGD5K7F4OJJ2DLCV6CA/db_iam_user“, these are the key components:
- ap-southeast-2 is the AWS Region that my Aurora MySQL cluster was created in
- 1234567890 represents my AWS account number
- cluster-WIK2GTMJGD5K7F4OJJ2DLCV6CA is the Aurora MySQL cluster resource ID
- db_iam_user is the database user I created in Aurora MySQL
Note: If you’re using the IAM console to create the policy, currently it displays an error for policies with the rds-db:connect action. You can ignore this error.
Finally, attach the preceding IAM policy to an IAM user or a role. In this example, we add the policy to an IAM user.
The policy that I created in the previous steps is named rds-connect-policy.
For more detailed steps on working with IAM policies and RDS, see Creating and Using an IAM Policy for IAM Database Access in the RDS documentation. For a step-by-step guide about how to connect to RDS with IAM authentication, check out the AWS Database Blog post Use IAM authentication to connect with SQL Workbench/J to Amazon Aurora MySQL or Amazon RDS for MySQL.
Authentication option 2 is using built-in database authentication control.
As an example, the database command following creates a user that is authenticated by the database engine.
When using this approach, we recommend using AWS Secrets Manager for database password rotation and application access to the password. Using AWS Secrets Manager means that you don’t need to have the database password hard-coded within your application code or configuration. It also enables you to apply automation to password rotation.
To learn how to use AWS Secrets Manager for password rotation, look at the AWS Security Blog post How to use AWS Secrets Manager to rotate credentials for all Amazon RDS database types, including Oracle. AWS Secrets Manager uses AWS Lambda functions to rotate RDS database passwords. For the details of the logic used by the Lambda functions to do this, see Overview of the Lambda Rotation Function in the Lambda documentation.
AWS Secrets Manager creates an AWS Lambda function in the same VPC and subnet group as the RDS database whose secret is being rotated. After the Lambda function is created, you reattach it to a subnet in the Management Zone (shown in the security zone modeling diagram preceding).
Database access control – After a user has been authenticated, the database engine checks the level of access that the user has for database resources, such as tables, views, stored procedures, and so on. You configure the level of access in the database by either assigning users predefined database roles or direct granting individual privileges.
The command following is an example of how you can grant data manipulation language (DML) access to a user. This is authorization, that is specifying which database resources the user can access. It is separate from authentication, which is about validating the identity of the user. Therefore, this authorization process remains the same regardless of whether the user is authenticated by the database or by using integrated IAM authentication.
Encryption and tokenization
Also key for database security are encryption and tokenization, discussed following.
Encryption at rest
Enabling encryption at rest is important. It ensures that the volumes underpinning the database and snapshots (if encrypted) can be read outside of the AWS account only with AWS KMS encryption key permissions explicitly granted.
To enable encryption at rest in all RDS database engines, you set a simple configuration option when you create your RDS database instance. Following is an example of how to enable encryption at rest in Aurora MySQL. Be mindful of the fact that you can’t enable encryption at rest after you create your RDS instance, so ensure that it is incorporated into your database provisioning process.
In addition to RDS encryption at rest, you can also enable encryption capabilities native to the specific database engine. Examples are Transparent Data Encryption (TDE) on RDS SQL Server and RDS Oracle and the SQL Server always encrypted option for SQL Server.
Enable RDS encryption at rest as part of the RDS launch wizard as follows.
Encryption in transit
Amazon RDS automatically creates a Secure Sockets Layer (SSL) certificate for each RDS database to enable client connections over SSL. Use RDS SSL certificates specific to your AWS Region or Regions to ensure that your applications are connecting to the RDS instance in the correct AWS Region. You can find links to download regional intermediate certificates for individual AWS Regions in Using SSL to Encrypt a Connection to a DB Instance in the RDS documentation.
Note: For an RDS Oracle database, you need to add the Oracle SSL option to the option group, as described in the RDS documentation.
Tokenization replaces sensitive data with unique identifiers. You use these identifiers to find the original sensitive data in another data source. In contrast, encryption applies a cypher to sensitive data in place so the data is encoded in a way that only authorized parties can read it.
Tokenization is an alternative to encryption that can help to protect certain parts of the data that has high sensitivity or a specific regulatory compliance requirement such as PCI. Separating the sensitive data into its own dedicated, secured data store and using tokens in its place can help avoid the potential cost and complexity of end-to-end encryption. It can also help reduce risk through the use of temporary, one-time-use tokens.
Tokenization is an application-level implementation concern. Typically, you implement it by using a dedicated data store to persist the mapping between tokens and their corresponding sensitive data values. The tokens are stored in application databases in place of sensitive data values and replaced by the application at runtime with real values from the dedicated token data store.
The diagram following illustrates an example of the tokenization process.
This example has the following steps:
- The application presents sensitive data such as a credit card number to the tokenization API.
- The tokenization API requests authentication of the application user.
- The authentication API authenticates the user,
- The tokenization API generates a token for the credit card number and stores and links the token and the credit card number in the tokenization database.
- The tokenization API returns the token to the application.
- The application stores the token in the application database in place of the credit card number.
Detective controls, described following, are also important to database security.
Detection of unauthorized traffic
There are a few different ways you can implement detection of unauthorized traffic, such as monitoring VPC flow logs and AWS CloudTrail logs with custom logic to identify anomalies. For details on VPC flow logs, see VPC Flow Logs in the VPC documentation. For details on CloudTrail logs, see Working with CloudTrail Log Files in the CloudTrail documentation.
Alternatively, you can enable Amazon GuardDuty to monitor network traffic on your behalf and apply machine learning to detect and alert you of anomalous behavior. Following is how you can enable Amazon GuardDuty on your account.
Navigate to Amazon GuardDuty in the AWS console. If this is the first time you’re enabling GuardDuty into your AWS account, you see the following page.
Turn on GuardDuty by choosing Enable GuardDuty.
Next, create CloudWatch rules that monitor and act on GuardDuty findings. You can set up notification alerts through Amazon SNS and also automated remediation actions through Lambda.
The following screenshot illustrates an example CloudWatch rule for a specified GuardDuty event that sends an email alert as an SNS topic.
To set this up, navigate to CloudWatch in the console and choose Rules to create a new rule. Then take these steps:
- Choose Create rule.
- Ensure that Event Pattern is selected.
- From the list, choose Build custom event pattern.
- Add the following JSON to the custom event window.
- In the Target section, add a new target for the CloudWatch rule by choosing Add target.
- Choose SNS topic from the list and choose your Amazon SNS topic name.
In configuration drift, a system’s current security configuration drifts away from its desired hardened state. Configuration drift can be caused by modifications to a system after its initial deployment.
The example following uses AWS Config to continually monitor the configuration of databases against baseline settings and send alerts when the configuration deviates from the baseline.
To set this up, navigate to AWS Config in the console and choose Rules to add a new AWS Config rule.
Note: You might go through an initial setup process if you’re enabling AWS Config for the first ever time.
Then take these steps:
- Choose Add rule.
- In the search box, type RDS.
- Choose the RDS config check that you want to enable.
Either search for and choose predefined rules for RDS, or create a new custom rule.
You can perform additional fine-grained audits for greater database security, as described following.
Control plane operations
As I said preceding, for RDS control plane operations refer to management functions on the RDS instance such as CreateDBInstance, ModifyDBInstance, and CreateDBParameterGroup. All Amazon RDS control plane operations are logged by CloudTrail and documented in the Amazon RDS API Reference.
Creating a new trail in CloudTrail enables all CloudTrail logs to be stored in Amazon S3 for audit purposes. For more information see Logging Amazon RDS API Calls with AWS CloudTrail.
To create a new trail, navigate to CloudTrail in the console and choose Trails.
The following screenshot illustrates an Amazon CloudWatch rule that matches all RDS API calls logged by CloudTrail and that can trigger a user-defined notification or action. To create this rule, follow these steps:
- Navigate to Amazon CloudWatch in the console.
- Choose Rules to create a new rule.
- Choose Create rule.
- Ensure that Event Pattern is selected.
- From the list, select Build custom event pattern.
- For Service Name, choose Relational Database Service (RDS).
- For Event Type, choose AWS API Call via CloudTrail.
- Select Any operation.
- In the Targets section, add a new target for the CloudWatch rule by choosing Add target.
- Choose SNS topic from the list and choose your Amazon SNS topic name.
Data plane operations
Again, data plane operations typically refer to actions that select, insert, modify, and delete the data in the database. In Aurora MySQL, you can track the data plane operations by enabling advanced auditing in the Aurora DB cluster parameter group. Database parameter groups act as a container for engine configuration values that are applied to one or more database instances.
Following is an example of how to enable advanced auditing in Aurora MySQL. For more information, see Using Advanced Auditing with an Amazon Aurora MySQL DB Cluster in the Aurora documentation.
Navigate to RDS in the console, and choose Parameter groups to create a new parameter group.
Create a custom parameter group for the Aurora cluster.
Modify the custom parameter group to enable server_audit_logging.
Modify the custom parameter group to enable logs export to CloudWatch. Working with CloudWatch Logs enables you to create custom notifications and actions. To do this, attach an appropriate CloudWatch Logs access role to the cluster parameter. You can find more information in Publishing Amazon Aurora MySQL Logs to Amazon CloudWatch Logs in the Aurora documentation.
Finally, associate this custom cluster parameter group with the Aurora MySQL cluster when you launch the database instance.
To learn more about enabling detailed audit logging in other RDS database engines, see the following documentation topics:
For a description and conceptual overview of swim-lane isolation, see the first post of this series. Implement swim-lane isolation to enforce strict subnet-level separation between databases and applications of different business domains. To implement swim-lane isolation, use a network ACL as described preceding in Security groups and network ACLs.
In this post, I demonstrate how to take the generic data security patterns described in the first post of this blog series and apply them to an RDS database. Doing this can help you create a strong security posture around your sensitive data.
I also show in this post how to protect your RDS database by using a layered approach to create defense in depth. This approach uses network security zones using network ACLs and subnets within your VPCs.
In addition, I also discuss how you can enable a combination of AWS preventative security controls within AWS networking, IAM, and database services. I cover how to set up detective controls using Amazon GuardDuty, AWS Config, and AWS CloudTrail to enable defense in depth for your data.
In the next and final post of this series, I show how to apply the same security concepts to Amazon DynamoDB.
About the Author
Syed Jaffry is a solutions architect with Amazon Web Services. He works with Financial Services customers to help them deploy secure, resilient, scalable and high performance applications in the cloud.