Deep Dive Into The AWS Web Application Firewall (WAF)

Daniel Glucksman
18 min readJul 7, 2020

--

The AWS WAF is a layer seven firewall that can be enabled to protect a Cloudfront distribution, an Application Load Balancer (ALB), or the API Gateway.

The function of a firewall is to allow or deny traffic based on a set of rules. Firewalls can operate on different levels of the OSI Model, as shown below:

The two most common firewalls are:

  • Network firewalls that operate at Layer 3 and only understand the source IP Address, port, and protocol. AWS Security Groups are a great example of this.
  • Application firewalls that operate at Layer 7 and can understand higher-level protocols such as an HTTP(S) request, including its headers, body, method, and URL.

The WAF can protect more than you might think!

Cloudfront allows the use of custom origins to serve content, which means you can have a WAF protect any server, even those not hosted on AWS.

The API Gateway can act as an HTTP proxy allowing a WAF to protect any non hosted AWS API as long as the traffic routes through the gateway.

The WAF Basics

At the top level, the WAF consists of a Web ACL that is assigned to one or more supported AWS resources. The Web ACL contains a collection of rules which determine whether a given request should be allowed or blocked. Those rules can be your own rules or provided by a 3rd party.

The protected AWS Resource forwards any request it receives to the WAF. If the WAF determines the request should be blocked, the AWS resource will generate a 403 response back to the client. If allowed, the request is forwarded onwards.

The 403 response body varies based on the AWS resource type. Some resources, such as CloudFront, allow you to customize the default message. The important thing is that there is no indication to an attacker that the WAF is the one who explicitly blocked the request.

As an example, an ALB response looks like this:

HTTP/1.1 403 ForbiddenServer: awselb/2.0Date: Sun, 21 Jun 2020 05:43:09 GMTContent-Type: text/htmlContent-Length: 134Connection: keep-alive<html><head><title>403 Forbidden</title></head><body bgcolor="white"><center><h1>403 Forbidden</h1></center></body></html>

When a request comes into the WAF, the rules are evaluated from top to bottom.

A rule can evaluate the URL, header, body, or method of an HTTP(S) request along with its Source IP and Country of Origin.

Evaluation can mean a string comparison, regex, size match(in bytes), or one of the built-in specialized operators for detecting SQL injection and Cross-Site Scripting attacks.

A rule allows you to apply multiple transformations to your text before its evaluated such as removing whitespace or decoding a URL.

Attackers try hard to confuse your defenses by masking their true intentions. For example, the following URL might look harmless on the surface

http://www.example.com/login?userid=frank%27%3b%20update%20users%20set%20password
%3d%270wn3d%27%3b — %00

but when URL decoded looks like a SQL injection attack.

http://www.example.com/login?userid=frank'; update users set password ='0wn3d';-

By applying a URL decode text transformation, your rule can be evaluated appropriately for its real intention.

— — — — — — — — — — — — — — — — —

You can use a Rate limit rule to throttle the number of requests from a single IP Address within a 5 minute period, and that can combine with any other condition. For example, you can apply a different rate limit based on the URL or country of origin.

If a rule matches a request, three actions can be taken; allow, block, or count. The count action is a kind of debug mode that will enable you to evaluate a rule without affecting your real traffic.

If the request matches the conditions of a rule with a “count” action, additional rules will continue to be evaluated.

If an allow or block rule is matched, the action will be applied, and additional rules will not be evaluated.

The Web ACL will apply a default action if no allow or block rules match a request.

Rules can be organized into rule groups, which you can reuse across Web ACLs.

You can write your own rules and/or use predefined managed rule groups provided by AWS or a 3rd party.

AWS provides free managed rule groups, which are a quick way to get yourself protected.

You can also use rule groups from 3rd party sellers, but those require a monthly subscription.

There is an upper limit to the number of rules you can use in a Web ACL. AWS assigns each rule a “capacity” based on its complexity. Certain rule conditions such as Regex, SQL Injection, or XSS Scripting are significantly more expensive than others.

By default, your Web ACL limit is 1500 units. You can request an increase from AWS support if you so desire.

.

The WAF allows you to log requests through a Kinesis Firehouse to a variety of AWS services such as an S3 Bucket, Redshift, or the Elastic Search Service.

You can also view a sampling of requests from the last 3 hours free of charge!

Every rule or rule group can emit Cloudwatch metrics enabling you to track the number of blocked, allowed, or counted requests.

Getting Started

The first step is to create a Web ACL that can be assigned to a supported AWS resource.

To create a Web ACL:

  1. Go to the WAF & Shield section of the AWS console.
  2. Choose Web ACLs from the left side menu.
  3. Click Create web ACL.

4. Choose the Name and CloudWatch metric name of your Web ACL. Both fields cannot be changed later on, so choose wisely.

5. Choose whether your Web ACL belongs to a specific region and can be shared by any supported resource in that region (ALB, API), or belongs to Cloudfront and can be shared by any Cloudfront distribution. You cannot share an ACL between Cloudfront and a regional resource type, nor can you share an ACL between regions. This choice is permanent and cannot be changed later.

When choosing Cloudfront, the region field is locked as Cloudfront operates on a global level and does not belong to a specific region.

6. The rest of the sections are optional, so we will skip them for now and proceed with creating the actual ACL. Keep clicking Next until you reach the final screen and then click Create web ACL.

Adding Rules to a Web ACL

The ACL we created above has no rules defined and permits the entry of all traffic by default. Not very useful is it! To add rules, you can either write them yourself and/or use predefined managed rule groups. Using managed rule groups is the easiest way to get started, so let’s start there.

Note: Using the AWS CLI, you can create a Web ACL using JSON to describe the rules. You can also view the JSON definition of existing Web ACL’s, making it easy to copy/paste between them if desired.

Using Managed Rule Groups
A managed rule group is a predefined set of rules which are managed by either AWS or a 3rd party vendor. The rulesets provide immediate protection from a variety of threats such as the OWASP Top 10 list and are continuously updated to ensure protection against new threats. The AWS provided rules are free to use, while the 3rd party ones require a paid subscription.

The advantage of managed rule groups is that you don’t need to write and maintain your own rules, which is important because the threat landscape is continuously changing. The disadvantage is that the rules operate in a black box; you cannot view or customize the rule definitions, and the rules can be automatically updated at any time. The good news is that the managed rule providers provide useful documentation, release notes, and also offer support when needed.

Let’s add some AWS managed rule groups since they don’t require a monthly subscription.

To add AWS managed rule groups, click Rules, Add rules, and then Add managed rule groups.

Expand the AWS managed rule groups section:

At a minimum, you should probably enable the baseline rulesets, which include the Core rules, Known bad inputs, Admin Protection, and the Amazon IP reputation list. You should also enable the OS rule corresponding to your server operating system. If you use a SQL database, enable the SQL database ruleset. Other rules should be enabled if they pertain to your specific use case. For more information about what each rule group does, see here.

Note: Remember, each managed rule group has an assigned capacity that contributes towards your limit of 1500 units.

To enable a rule, simply click the switch, which says, “Add to web ACL.”

I recommend that you always start in count mode, which will allow you to safely evaluate the effect a rule group will have on your traffic without actually blocking it. To do that, click “Set rules action to count.”

Once you have confirmed the rule is safe to use, you can turn off count mode by editing the rule and disabling it. (see below)

After clicking “Add Rules” at the bottom of the screen, you will be asked to set the order in which rules should be evaluated.

Click “Save” to finish the process.

Now you’re probably curious as to what types of rules are within the rule groups you just enabled. By editing the managed rule group, you can view the rule names but cannot see or customize the rule definitions. The rule names are pretty descriptive and typically give you a sense of what a rule may do.

To view the rules

  1. Click Rules
  2. Select the rule group you are interested in
  3. Click edit

Below is the list of rules within the AWS managed rule group called “Core ruleset.” You can put an individual rule into count mode by clicking “Override rules action” or do it for all rules by clicking “Enable count mode.”

When you are done, click “Save Rule.”

Adding your own rules

To add your own rule, click “Rules,” then “Add rules,” then “Add my own rules and rule groups.”

Choose Rule Builder

Rules can be defined visually or through the provided JSON editor.

Using the JSON editor allows you to define more complex nested rules as compared to the visual editor, which allows just one level of nesting. It’s also great for copying rules from other Web ACLs or online tutorials.

For this article, we are going to use the visual editor only.

Enter the name of the rule which must be unique and cannot be changed later on.

There are two rule types; Regular and Rate Limit. A Rate limit rule will throttle requests from a given IP Address within a 5 minute period if the conditions of the rule match the request. If you are not looking to rate limit, choose “Regular Rule.”

You can have multiple conditions within a single rule using logical operators. (AND, OR, NOT). You can also nest logical operators but not within the visual editor.

If you choose the rate limit rule type, decide whether to simply rate limit all requests or combine it with other conditions.

Specify the rule conditions; In this rule, we are blocking any request in which the query string matches any of the standard SQL injection attack techniques.

Add any Text transformations which should be applied before evaluating the rule.

Choose the rule action; allow, block, or count. For rate limit rules, the “Allow” action is not available.

Associating a Web ACL to an AWS resource

  1. Click Associated AWS resources
  2. Click Add AWS resources

3. Select the type of AWS resource.

Note: Remember when you created the Web ACL you choose upfront, whether it’s for a Cloudfront distribution or a regional resource type. Your choices here will reflect that.

4. Choose the specific resource you want to apply the Web ACL too.

5. Click Add

Evaluating rules with the count action

Any new rule you introduce, whether managed or unmanaged, has the potential to block legitimate traffic.

For example, the AWS managed core ruleset has a rule which blocks all requests without a user agent header. What if you have automated scripts that don’t set this header?

When evaluating a rule in count mode, it’s essential to make sure the rule is placed before any other non-count rules. The reason is that once a block or allow rule is matched, no further rules will be evaluated even if they are count rules. This may cause you to think your new rule is safe to use when, in reality, it’s never been evaluated.

Step 1: Enable Count Mode

The process for putting a rule into count mode differs depending on whether its a free-standing rule or part of a rule group.

Count mode for Rule groups

To put a rule within a rule group into count mode:

  1. Click Rules
  2. Select the rule group to edit
  3. Click Edit

4. Choose the rule you want to set to Count mode

5. Click “Override rules action.” This will override the allow or block action set for this rule.

6. To put the entire rule group into count mode slide, the slider at the top, which says “Enable count mode.”

Count mode for Individual Rules

To put a single rule into count mode

  1. Follow steps 1–3 above.

2. Change the action to “Count.”

3. Click “Save Rule.”

Step 2: Evaluate Results

Once your rules are in count mode, there are three ways to analyze the effect they would have on your traffic; Sampled Requests, WAF Logs, and Cloudwatch Metrics.

Sampled Requests

This is the quickest and cheapest way (it’s free!) to evaluate.

AWS can capture a sampling of the last 100 requests per rule or rule group in which a match was identified within the previous 3 hours.

To get started:

  1. Go to the overview screen of your Web ACL and find the box labeled “Sampled Requests.”

2. Choose the rule or rule group from the filter drop-down in the top right corner for which you want to see the matched requests.

3. The table will show any request which matched the rule or rule group along with the action applied. Here you want to analyze the traffic to see whether legitimate requests are being blocked or allowed.

For each match, you can view details about the request, such as the source IP, request headers, URL, and Timestamp.

Note: The data is delayed for about 15 minutes.

Sample requests are enabled by default. You can turn it off or disable it for specific rules or rule groups if you so choose.

Logs

The WAF can log every incoming request to a Kinesis Firehose who’s destination can be set to a variety of AWS services such as S3, Redshift, or Elastic Search.

Depending on the destination chosen, you would then query the data to find patterns and to perform further analysis.

An example log file is below:

{
"timestamp":1593579912374,
"formatVersion":1,
"webaclId":"arn:aws:wafv2:us-east-1:506565459091:regional/webacl/test-waf/abaefbdb-0bdb-4f7b-9123-d8074cd172f6",
"terminatingRuleId":"Default_Action",
"terminatingRuleType":"REGULAR",
"action":"ALLOW",
"terminatingRuleMatchDetails":[
],
"httpSourceName":"ALB",
"httpSourceId":"506565459091-app/test-alb/20291d68df8869b2",
"ruleGroupList":[
{
"ruleGroupId":"AWS#AWSManagedRulesAdminProtectionRuleSet",
"terminatingRule":null,
"nonTerminatingMatchingRules":[
],
"excludedRules":null
},
{
"ruleGroupId":"AWS#AWSManagedRulesCommonRuleSet",
"terminatingRule":null,
"nonTerminatingMatchingRules":[
],
"excludedRules":[
{
"exclusionType":"EXCLUDED_AS_COUNT",
"ruleId":"NoUserAgent_HEADER"
}
]
}
],
"rateBasedRuleList":[
],
"nonTerminatingMatchingRules":[
{
"ruleId":"test-individual-rule",
"action":"COUNT"
}
],
"httpRequest":{
"clientIp":"1.1.1.1",
"country":"US",
"headers":[
{
"name":"Host",
"value":"test-alb-1445621045.us-east-1.elb.amazonaws.com"
},
{
"name":"Content-Length",
"value":"71"
},
{
"name":"content-type",
"value":"application/json"
}
],
"uri":"/",
"args":"",
"httpVersion":"HTTP/1.1",
"httpMethod":"POST",
"requestId":null
}
}

You can choose to redact certain parts of the HTTP request if, for example, they contain sensitive information.

Cloudwatch

Cloudwatch allows you to track high-level metrics for the number of blocked, counted, and allowed requests.

You can enable or disable Cloudwatch metrics on a per rule or rule group basis. You can also combine multiple rules under the same metric name.

Step 3: Remediation

Once you determine that a rule or ruleset is blocking your legitimate traffic, your options will depend on whether the rule is managed or unmanaged.

For managed rules, your options are to leave the specific rule disabled in count mode, contact AWS or the 3rd party vendor support, or write an additional higher priority rule to allow the legitimate traffic through explicitly.

For unmanaged rules, you can change the rule definition or write an additional higher priority rule to allow the traffic through explicitly.

Rule Groups

Rule groups are a great way to organize rules, and can you be reused across ACL’s. They need to belong to a specific region or Cloudfront and cannot be shared between the two.

Note: Once you create a standalone rule within a Web ACL, there is no way to assign it to a rule group. You first need to to create the rule group and then add the rule within. You can always delete the free-standing rule and copy the JSON rule definition to a new rule within a group.

Rule groups have two noteworthy limitations, which are that they cannot contain a rate limit rule, and you cannot nest rule groups.

To create a Rule group:

  1. Go to the WAF & Shield section of the AWS console.

2. Click Rule groups on the left-hand side.

3. Click Create rule group.

Enter the Name and Cloud watch metric name. Choose wisely as the name cannot be changed.

Rule groups belong to a specific region and cannot be shared across regions. If you are using Cloudfront, make sure you choose the Global (Cloudfront) option. You cannot share a rule group between Cloudfront and another resource type.

You need to determine upfront the maximum capacity of all rules you intend to add to this group. This field cannot be changed.

If you set the capacity too low, then you will not be able to add additional rules in the future and will receive the following error:

If you set it too high, then your rule group will consume unnecessary valuable capacity within your Web ACL limit.

My advice is to define as many rules upfront so you can see what the capacity is and then estimate how many additional rules you may need to add in the future.

Adding a rule group to a Web ACL

To add your rule group, click “Rules,” then “Add rules,” then “Add my own rules and rule groups.”

Choose Rule Group

This next part can get a bit confusing without a little background as to how rule groups work under the hood. When adding a rule group to a Web ACL, a special rule is created that references the rule group. This name field identifies that special rule.

Choose the rule group from the drop-down, optionally set the rule group to start in count mode, and then click “Add Rule.”

Pricing

  • $5 per Web ACL, even if it’s not associated with an AWS resource.
  • $1 for every unmanaged rule. It doesn’t matter if it’s in a rule group or not.
  • $1 for every rule group associated with a web ACL (managed or unmanaged).
  • $0.60 per million requests that come through the WAF.
  • 3rd party managed rule groups have a subscription fee

Note: Rules cannot be shared across regions or between Cloudfront and Regional resources, so you will pay double if you want to maintain the same rules across them.

The WAF Gotchas

Below are a couple of noteworthy gotchas.

Request Body Limit

The WAF will only evaluate the first 8KB of a request body. To mitigate this, you can determine whether any existing request exceeds this amount by setting up a count rule, as shown below:

Then you can decide whether to block requests above the 8KB limit and whether it should apply to all requests or only specific ones. For example, you may want to block all requests except for those doing file uploads.

Everything belongs to a region.

The Web ACL and Rule Groups all need to belong to either a region or Cloudfront. This makes it challenging to use the same WAF rules across regions or between Cloudfront and another resource type like an ALB.

Possibility of illegitimate traffic being allowed through Cloudfront

The WAF documentation mentions that Cloudfront may bypass the WAF if it’s slow to respond or times out. This doesn’t seem like it would happen often, but something to be aware of nevertheless. This does not apply to the API Gateway or ALB.

Deleting an ACL will delete all rules within

Rules belong to a Web ACL, while Rule Groups exist independently and are just referenced from the Web ACL. If you delete a Web ACL, it will permanently delete without warning any rules, not part of a rule group.

Only ALB’s in the cloud is supported.

Only ALB’s within a physical AWS region is supported, not those you are running on-premise such as through AWS Outposts.

ALB + WAF Max Request Rate

The WAF associated with an ALB has a maximum request rate of 25,000/second.

Minimum Request Rate For Rate Limit Rule

A Rate limit rule can block IP’s which exceed a certain number of requests within a 5 minute period. That number cannot be less than 100.

Maximum IP’s for Rate Limit Rule

The Rate limit rule can block a maximum of 10,000 IP Addresses.

I hope you enjoyed this article. Feel free to leave any comments.

--

--