How to Mitigate DDoS Attacks with AWS WAF Rate-based Rule

How to Mitigate DDoS Attacks with AWS WAF Rate-based Rule

Takahiro Iwasa
(岩佐 孝浩)
Takahiro Iwasa (岩佐 孝浩)
4 min read
WAF

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. It allows you to set thresholds for HTTP requests from each IP address. This post describes how to use it.

Overview

Please keep the following in mind.

  • 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

Create a CloudFormation template with the following content. The key points are Limit and AggregateKeyType (lines 53-54). The example here uses 100 as a rate limit.

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

Deploy the CloudFormation stack with the following command.

aws cloudformation deploy \
  --region us-east-1 \
  --stack-name aws-waf-rate-based-rule-sample \
  --template-file template.yaml

Please upload a sample index.html to the S3 bucket with the following command.

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

AWS WAF rate checking interval is 30 seconds, so send requests every second for 130 seconds or a bit more. You should see the requests blocked by the AWS WAF rule, returning 403 Forbidden responses.

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
Request: 1
<html><body>Hello World!</body></html>

Request: 2
<html><body>Hello World!</body></html>

Request: 3
<html><body>Hello World!</body></html>

...

Request: 126
<!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

Clean up the provisioned AWS resources with the following command.

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

Footnotes

  1. https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-rate-based-high-level-settings.html

  2. https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-rate-based-request-limiting.html

  3. https://docs.aws.amazon.com/waf/latest/developerguide/listing-managed-ips.html

Takahiro Iwasa
(岩佐 孝浩)

Takahiro Iwasa (岩佐 孝浩)

Software Developer at iret, Inc.
Architecting and developing cloud native applications mainly with AWS. Japan AWS Top Engineers 2020-2023