Enhance Your Web Protection: AWS WAF Essentials
Tags: aws, cloudcomputing, devops, security
Published: 2024-04-15 09:46:56


### <u>What is the AWS WAF?</u>

The AWS WAF which stands for Web Application Firewall is a tool that lets you monitor requests to your web application resources. It protects some of the following AWS Resources like:

* Amazon Cloudfront Distribution
* Application Load Balancer
* Amazon API Gateway REST API and a couple of other resources.


### <u>What does the AWS WAF do?</u>

It basically lets you control access to your content, Based on whatever conditions you specify e.g the IP addresses the requests come from, the path being requested or the location the requests originate from , the service associated with whichever resource is being protected either gives the right response, or a forbidden permission response ( also known as the 403 error) or a custom response configured by the user.


### <u>How does the AWS WAF work</u>?

The WAF works to control how your protected resources respond to web requests and it does that using a Web ACL (Access Control List) and associating it with the resources to be secured. The resources then forward any incoming requests to the ACL for inspection.

With the web access control list,you define rules for traffic patterns which you look for in those requests,and then set the action to be taken on requests that match the patterns, which can include:

- Allow the requests to go to the protected resource for processing and response.

- Block the requests.

- Run CAPTCHA or challenge checks against requests to verify human users


####<u> Components of the WAF</u>

These are some of the components that make up the AWS WAF

1. Web ACL: You use this to protect your AWS resources via rules, The rules help define the criteria for the requests coming in and also the action to take on requests that match the rules, You can set default actions on matched requests as listed above. As an example, you can set a rule to block all requests coming from a specific IP address.

2. Rules: These are the building blocks of the Web ACL and they determine what actions are to be taken on requests.

3. Rule Groups: A rule group is a reusable set of rules that you can add to a web ACL.


### <u>Configuring the AWS WAF (Walkthrough)</u>

To show the capabilities of the AWS Web Application Firewall, we will create an EC2 Instance running Wordpress, place it behind an Application Load Balancer and associate a WAF Web ACL to it.

We will then explore rules we can create , as well as set up logging for the WAF.


##### <u>Step 1 - Creating the EC2 Instance.</u>

First we create the EC2 Instance , using a wordpress image gotten from this link: [https://bitnami.com/stack/wordpress/cloud/aws/amis](https://bitnami.com/stack/wordpress/cloud/aws/amis)

Copy the image id for the region you want to deploy the instance in.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/83r6r0fqa1j3ik5qudak.png)

Login to the AWS EC2 Console: [EC2 Console](https://us-east-1.console.aws.amazon.com/ec2/home?region=us-east-1#Home:)

Click on launch instance.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/un5b19md301d0re3bjxq.png)

Give the instance , the name **wordpress** .

Under **Application and OS Images**, Paste the image ID you copied and search for it under Community Images.

Select the **Image**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/13ogvtas649eb9tw3f7j.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xm5xui7ghn3j7s2kd1di.png)

Leave the **instance type** as default (t2.micro) which is free tier 

Set the **key pair login** to "Proceed without a keypair" as we don't need access to the instance.

Under **Network settings**, click edit


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rabdyyrpdwhb712aatvq.png)

Make sure the **default VPC** is selected and ensure auto assign public IP is enabled.

Create a **new security group** named **wordpress-waf** and change the security group rule 1 to HTTP.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xosbvba2if4ljirqa4kz.png)

Leave all other settings as default and launch the instance.

Once the instance has been created, copy the public ip and navigate to it in your browser.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j90stuymcnbkug02wxcz.png)

You should see the wordpress default page.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qyi7d7od56lhgwafsyfn.png)


#### <u>Step 2 - Creating a target group.</u>

Go back to the EC2 Console: [EC2 Console](https://us-east-1.console.aws.amazon.com/ec2/home?region=us-east-1#Home:)

Go to target groups and click on create target group.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eacb5ngviy9o0vnjvgp4.png)

Choose the **target type** as instances.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ivk2iyd51gyynctfw390.png)

Set the **target group name** to be wordpress

Leave everything else as default and click Next.

Select the instance created previously and select include as pending below.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jd2t9vgpkysnhjh2q8qd.png)

Click on create target group.


####<u>Step 3 - Creating an Application Load Balancer.</u>

On the EC2 Console Page , click on load balancers and then create load balancer.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ubk8z42vib692k9agfqj.png)

Select the **Application Load Balancer**.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/igy0sh8jm84az9z9l056.png)

Set the Load Balancer name to wordpress-lb

Make sure the VPC is default and select all subnets under Network mapping.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/irte2vlcp93sgb9lo69g.png)


Under **Security Groups**, select the wordpress-waf group we created


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k4nt4573fmz357782pkf.png)


Under **Listeners and Routing** , change the action for HTTP to forward to the wordpress target group we created earlier.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z9n06ypts75vl4d7y5es.png)


Leave all other settings and create the load Balancer.

Once the ALB ( Application Load Balancer ) is created , Paste the DNS Name in the Browser, you should see the same wordpress default page as earlier.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m3ycqxx78gm0nkuagvji.png)



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vu47s2c1bjq3wh3jaqk2.png)


#### <u>Step 4 - Creating an s3 Bucket for WAF Logs.</u>

Head to the S3 Console: [S3 Console](https://us-east-1.console.aws.amazon.com/s3/home?region=us-east-1)

Go to Buckets and click create bucket.

S3 buckets for aws waf logs must begin with **aws-waf-logs-** , so name your bucket with that prefix.

Make sure the bucket is in the same region as your EC2 Instance.

Leave all other settings as default and create the bucket.



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/stgzfrgud1j9qr2puoa9.png)



#### <u>Step 5 - Setting up the WAF.</u>

Go over to the WAF Console, which can be found here: [https://us-east-1.console.aws.amazon.com/wafv2/home?region=us-east-1](https://us-east-1.console.aws.amazon.com/wafv2/home?region=us-east-1)

Ensure that the region is the same as the region your instance is in, in my case, its the us-east-1 region.

First, we would create an IP Set, which is basically a group of IP's ( which is used commonly for IP addresses you want to allow).  IP Sets can contain up to **10,000 CIDR ranges**, which makes allowing or blocking large numbers of networks very easy.

Check your IP address using this link: 

[https://checkip.amazonaws.com/](https://checkip.amazonaws.com/) and copy your IP Address.

Go to **IP Sets** and create IP Set.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rvh0pwtqyw0kmbkeqedf.png)

Set the IP Set name to **home-ip**

In the IP Addresses box, add the IP you copied with the /32. e.g 172.35.40.78/32.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pmey2gxol9crstt5kbfa.png)


Create the IP Set.

Go to **Regex Pattern Sets** and click on create regex pattern set.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mvf8ran380xkn5bdacku.png)


Set the regex pattern name to **no-wp-files**

In the Regular expressions box, enter:

```
(wp\-login\.php)$
(.*wp\-config.*)
(xmlrpc\.php)
```

Click on create regex pattern set.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aa8dan2hduyiysavzqu0.png)



Go to **Web ACL**  and click on create Web ACL


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0p9hkr1h2p5l212w2kbk.png)

Under **Resource Type**, click Regional Resources.

Set the Name to **wordpress-acl**.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4n8pdwb3pmurirmhjy4h.png)


Under **Associated AWS Resources**, click Add AWS Resources.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fslrj5hfhr2ffi9bs6q7.png)


Under AWS Resources, click on Application Load Balancer and select the ALB we created earlier.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mhuyrh7zog0e9hvurur5.png)

Click on next.

Under **Rules**, click on Add Rules, and then Add Managed rule groups.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wik8ig5tmdcipk6xh67e.png)

Expand AWS Managed Rule Groups and you will see a list of WAF rule groups that are supplied and maintained by AWS.

Under the Free Rule Groups, Select the following.

* Core rule set
* PHP application
* SQL database
* WordPress application

Each rule group has a description showing what kind of attacks the rule group helps protect against.

Given that our infrastructure is centered around a WordPress application, it's essential to prioritize the WordPress rules for enhanced protection. Additionally, since WordPress is PHP-based, integrating PHP application rules is crucial. As our WordPress database relies on MySQL, implementing the SQL database rule set becomes imperative to safeguard against potential SQL Injection attacks, among other threats.

The “Core rule set” contains the most rules, and protects against common attack methods provided by the open source OWASP organisation

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y7vxycvbalwkl8ncibol.png)

Click on Add rules.

**<u>Note:</u>** each rule group has a “capacity” value. Each Web ACL has a maximum capacity of 1500 “WebACL Capacity Units” (WCUs), and each rule we add uses up some of those units. E.g The SQL rule has a capacity value of 200.

Under the **Default web ACL action for requests that don't match any rules** , set it to Allow, this is because we only want to block traffic matching any of our rules.

Click on Next.

You can also set the rule priority for the rules selected earlier. Web ACL rules are executed top to bottom, so the first rule is executed first and so on.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/12lz7bf2oblgmdzw0etc.png)

Click Next and ensure that **Request sampling options** is set to enabled.

Create your Web ACL

Once the WAF has been created, click on it and under Logging and metrics, click Enable


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uhnycnjb8tu4j04c83l6.png)

Then click on s3 bucket as logging destination and select the bucket we created earlier.

Then save.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g360r6shl6tmeodqt6f8.png)


#### <u>Step 6 - Testing the WAF.</u>

Open the ALB URL that we visited earlier.

We should be able to view the wordpress page, and even the blog and admin page (/sample-page/ and /wp-admin).

If we try a simple SQL Injection Attack like : `/wp-login.php?user=1+ORDER+BY+10` , the WAF should stop us with a code 403 ( permission denied).


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wxo94x2zjxsxi08l0h76.png)

Once verified that the WAF rules are working as expected.

Go back to the WAF Console, click on your WAF and navigate to the Sampled requests.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6bu56pc6b4qcy7966zsm.png)

If you search for your IP Address, you should see the requests you made,  the actions applied on those requests (ALLOW/BLOCK) and the rule that cause any block action.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iq1ygxv7qwzp3lwyvpph.png)


#### <u>Step 7 - Adding Custom Rules to your WAF Web ACL.</u>

We don’t want our WordPress login page or XML-RPC page being accessed by anyone, so we’re going to use our regex pattern we created earlier to block any requests to `/wp-login.php` or `/xmlrpc.php`

Go to your WAF console: [https://us-east-1.console.aws.amazon.com/wafv2/home?region=us-east-1](https://us-east-1.console.aws.amazon.com/wafv2/home?region=us-east-1)

Click on your wordpress web acl.

Navigate to rules. **Then click on Add rules, and add my own rule and rule groups**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wzyfnqmlunbzppa8ju8c.png)

Leave the rule type as **Rule Builder** and name it **no-wp-files**.

Leave the type as a regular based rule.

Under the block **If a request matches the statement**, set the inspection option to **URI Path**

Set **Match Type** to “Matches pattern from regex pattern set”

Set **Regex pattern** set to the pattern set we created earlier (no-wp-files).

Set **Text transformation** to “Lowercase” ,this changes everything in our inspection option which is the URI path to lowercase. e.g `/wp-login.php` and `/WP-LOGIN.PHP` will be seen by the WAF as `/wp-login.php`


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zekk6bphu2dfa483otdy.png)


Under **Action**, Select Block


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iwkkcijsdh1sgp8spj0b.png)

On the next page, we need to move our new rule to the top of the priority list. Remember that rules in Web ACL are executed in order of top to bottom.

Select your new rule, and keep clicking **Move up** until it’s at the top of the list.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g23y5kjmcdmf5qdyh58x.png)

Then save

Now if we try accessing either the `/wp-login.php`,`/wp-admin` or `/xmlrpc.php`. We will get a **403 forbidden message**.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/johy3pd92f5e1akz7qat.png)

If we check the sampled requests of the AWS WAF console, we would see our IP and the path requested, with the action shown as **BLOCK**.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h9ig2z9cxl916o3op77c.png)

We can also add many other custom rules such as:

* Allowing our home ip access to the application, since it is a trusted ip
* Allowing requests coming from a particular country, while blocking all other requests.

#### <u>Step 8 - Viewing our WAF Logs.</u>

We can view our WAF Logs by going to the bucket we created in the S3 Console: [S3 Console](https://us-east-1.console.aws.amazon.com/s3/home?region=us-east-1).

Go to buckets and click on our waf logs bucket.

The directory structure will be:

`/AWSLogs/[ACCOUNT ID]/WAFLogs/[REGION]/[WEB ACL]/[YEAR]/[MONTH]/[DAY]/[HOUR]/[5 MINUTE BLOCK]/[LOGS]`


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzl9kqj8igtxpk1c9l9f.png)

You can download the logs (which is in the .log format) and view it using the json pretty print website: [https://jsonformatter.org/json-pretty-print](https://jsonformatter.org/json-pretty-print), as the json file might be difficult to read.

An example of a log can be this:

```json
{
  "timestamp": 1713169621018,
  "formatVersion": 1,
  "webaclId": "arn:aws:wafv2:us-east-1:567635759120:regional/webacl/wordpress-acl/24ce438c-a4b9-467f-8b4c-ba5ab8d56db3",
  "terminatingRuleId": "no-wp-files",
  "terminatingRuleType": "REGULAR",
  "action": "BLOCK",
  "terminatingRuleMatchDetails": [
    {
      "conditionType": "REGEX",
      "location": "URI",
      "matchedData": null,
      "matchedFieldName": ""
    }
  ],
  "httpSourceName": "ALB",
  "httpSourceId": "567635759120-app/wordpress-lb/3630369d4fc6a356",
  "ruleGroupList": [
    {
      "ruleGroupId": "AWS#AWSManagedRulesPHPRuleSet",
      "terminatingRule": null,
      "nonTerminatingMatchingRules": [],
      "excludedRules": null,
      "customerConfig": null
    }
  ],
  "rateBasedRuleList": [],
  "nonTerminatingMatchingRules": [],
  "requestHeadersInserted": null,
  "responseCodeSent": null,
  "httpRequest": {
    "clientIp": "154.118.22.58",
    "country": "NG",
    "headers": [
      {
        "name": "Host",
        "value": "wordpress-lb-1743095222.us-east-1.elb.amazonaws.com"
      },
      {
        "name": "Connection",
        "value": "keep-alive"
      },
      {
        "name": "Upgrade-Insecure-Requests",
        "value": "1"
      },
      {
        "name": "User-Agent",
        "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
      },
      {
        "name": "Accept",
        "value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
      },
      {
        "name": "Accept-Encoding",
        "value": "gzip, deflate"
      },
      {
        "name": "Accept-Language",
        "value": "en-GB,en-US;q=0.9,en;q=0.8"
      }
    ],
    "uri": "/xmlrpc.php",
    "args": "",
    "httpVersion": "HTTP/1.1",
    "httpMethod": "GET",
    "requestId": "1-661ce4d5-403eac0b43ee412a6810f599"
  }
}
```
Here in this log file,you see the ACTION set to block and the terminating rule ID is the no-wp-files custom rule we created earlier.

You can also see all of our request headers are there, as well as the URI, query strings, etc. This can be removed if you prefer.

#### <u>Step 9- Clean Up.</u>

Ensure all the resources created during this walkthrough are deleted. These include :

* Web ACL 
* IP Sets
* Regex Pattern Sets
* S3 Bucket
* EC2 Instance
* Target Groups
* Load Balancer
* Security Groups

I trust that this article has effectively demonstrated the capabilities of AWS WAF and provided you with a deeper understanding of its functionality and implementation.

### <u>References</u>

[AWS WAF Documentation](https://docs.aws.amazon.com/waf/latest/developerguide/how-aws-waf-works.html)
[Andrew Cantrill's WAF Demo Guide](https://github.com/acantril/learn-cantrill-io-labs/blob/master/aws-waf/readme.md)