A Guide to Mitigating DDoS Attacks with AWS WAF Rate-Based Rules
Introduction
AWS WAF provides protection against layer 7 attacks such as SQL injection and XSS. In addition, you can use rate-based rules to mitigate DDoS attacks. These rules allow you to set thresholds for HTTP requests from each IP address. This post describes how to use them effectively.
Overview
Here are a few key considerations:
- The minimum rate that you can set is 100. 1
- AWS WAF checks the rate of requests every 30 seconds, and counts requests for the prior 5 minutes each time. Because of this, it’s possible for an aggregation instance to have requests coming in at too high a rate for up to 30 seconds before AWS WAF detects and rate limits the requests for the instance. 2
- The maximum number of IP addresses that AWS WAF can rate limit using a single rate-based rule instance is 10,000. If more than 10,000 addresses exceed the rate limit, AWS WAF limits those with the highest rates. 3
Creating AWS Resources
You can use the following CloudFormation template to create the necessary resources. Key settings include Limit
and AggregateKeyType
(lines 53-54). In this example, a rate limit of 100 is configured.
AWSTemplateFormatVersion: 2010-09-09
Description: AWS WAF Rate-based rule sample
Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub aws-waf-rate-based-rule-sample-${AWS::AccountId}-${AWS::Region}
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: TRUE
BlockPublicPolicy: TRUE
IgnorePublicAcls: TRUE
RestrictPublicBuckets: TRUE
S3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref S3Bucket
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: cloudfront.amazonaws.com
Action: s3:GetObject
Resource: !Sub arn:aws:s3:::${S3Bucket}/*
Condition:
StringEquals:
"AWS:SourceArn": !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}
# AWS::WAFv2::WebACL must be deployed in us-east-1.
WAFv2WebACL:
Type: AWS::WAFv2::WebACL
Properties:
Name: aws-waf-rate-based-rule-sample
DefaultAction:
Allow: { }
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: aws-waf-rate-based-rule-sample
Scope: CLOUDFRONT
Rules:
- Name: rate-based-rule
Priority: 0
Action:
Block: { }
Statement:
RateBasedStatement:
Limit: 100
AggregateKeyType: IP
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: rate-based-rule
CloudFrontOriginAccessControl:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Name: aws-waf-rate-based-rule-sample
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
DependsOn: CloudFrontOriginAccessControl
Properties:
DistributionConfig:
Origins:
- Id: !GetAtt S3Bucket.DomainName
DomainName: !GetAtt S3Bucket.DomainName
OriginAccessControlId: !Ref CloudFrontOriginAccessControl
S3OriginConfig:
OriginAccessIdentity: ''
DefaultCacheBehavior:
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
TargetOriginId: !GetAtt S3Bucket.DomainName
ViewerProtocolPolicy: allow-all
Enabled: true
ViewerCertificate:
CloudFrontDefaultCertificate: true
MinimumProtocolVersion: TLSv1
WebACLId: !GetAtt WAFv2WebACL.Arn
DefaultRootObject: index.html
Deployment Steps
1. Deploy the CloudFormation stack
aws cloudformation deploy \
--region us-east-1 \
--stack-name aws-waf-rate-based-rule-sample \
--template-file template.yaml
Scope: CLOUDFRONT
must be deployed in the us-east-1
region.
2. Upload a sample index.html
to the S3 bucket
echo '<html><body>Hello World!</body></html>' > index.html
aws s3 cp index.html s3://aws-waf-rate-based-rule-sample-<ACCOUNT_ID>-us-east-1
Testing
Since the AWS WAF rate-checking interval is 30 seconds, send requests every second for 130 seconds or longer. Requests exceeding the configured limit will be blocked with a 403 Forbidden response.
AWS WAF checks the rate of requests every 30 seconds, and counts requests for the prior five minutes each time.
for i in `seq 1 130`; do
echo "Request: $i"
curl https://<CLOUDFRONT_DOMAIN>/
echo "\n"
sleep 1
done
Example blocked response:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>403 ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
Request blocked.
We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
<BR clear="all">
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: xxxxxxxxxxxxxxxxxxxx
</PRE>
<ADDRESS>
</ADDRESS>
</BODY></HTML>
Cleaning Up
After testing, clean up the AWS resources using the following commands:
aws s3 rm --recursive s3://aws-waf-rate-based-rule-sample-<ACCOUNT_ID>-us-east-1
aws cloudformation delete-stack \
--region us-east-1 \
--stack-name aws-waf-rate-based-rule-sample
Conclusion
By utilizing the rate-based rule in AWS WAF, you can mitigate DDoS attacks to a certain extent. However, combining this approach with additional solutions like AWS Shield Advanced is recommended for more comprehensive protection.
I hope this post helps you safeguard your applications against potential threats.
Happy Coding! 🚀