Denying connections originating from default AWS domain using WAF

Photo by Kyle Glenn on Unsplash

What is WAF? WAF stand for Web Application Firewall and works on layer 7 of the OSI model. It can allow or block requests based on request header, host, request body, query parameter, IP address, HTTP method, cookies, etc.

AWS WAF is a fully managed application layer firewall service that can be attached to CloudFront, API Gateway, Application Load Balancer and few other services too.


Background

In most cases, whenever we deploy our business solutions behind a Load Balancer, API Gateway or a CloudFront distribution which is mapped to a custom domain, we forget to lock down access to our business applications only via the custom domain. Due to this, if someone discovers the underlying URL of a Load Balancer, API Gateway or CloudFront they can sometime bypass security checks or even routing logic. Let’s understand the impact with two different scenarios.

In the first scenario, let’s assume our business application is hosted behind an HTTP API Gateway and we are serving it via a CloudFront distribution with the WAF attached to the CloudFront distribution as it serves as an entry point. In this case, if someone is able to discover the CloudFront URL they will not be able to bypass the WAF security checks but what if someone discovers the API Gateway URL. This will expose our business application to all sorts of application layer attacks.

In the second scenario, let’s say our business application is hosted behind an Application Load Balancer that performs host based routing. In this case, if someone discovers the load balancer URL the routing logic will fail because we are routing requests based on custom domain.

Let’s see how we can use AWS WAF to block direct access to AWS default domains and protect our applications from the above mentioned incidents.


WAF

Login to AWS Management Console and let’s navigate to WAF & Shield page and create a web ACL to overcome the above discussed issues. Click on the Create web ACL button to get started.

Fig 1. WAF Landing Page

In Step 1, provide a name for your web ACL and select the resource type depending on which resource you will be attaching the web ACL to. Make sure to also select the correct region from the below dropdown. It isn’t required to attach the resource(s) right away so we can skip attaching the resources at this point and proceed ahead but feel free to select the AWS resources if you want to from the list.

Fig 2. Web ACL Details

In Step 2, we get the option to add managed and/or custom rules. For this demo, let’s add the rule(s) after creating the web ACL but this doesn’t have to be the case every time. We can stick to Allow as the default web ACL action for this demo and proceed to next step.

Fig 3. Web ACL Rules

Since we did not create any rule in step 2 let’s click Next and move ahead.

Fig 4. Web ACL Rule Priority

In Step 4, we can leave the sampling options as disabled as this is just a demo.

Fig 5. Web ACL Metrics

In Step 5, review all the inputs and create the web ACL.

Fig 6. Create Web ACL

After the web ACL is created click on its name and switch to the Rules tab so that we can add a block rule for AWS owned domains.

Fig 7. Web ACL Rules Tab

Now, click on the Add rules dropdown button and then click on Add my own rules and rule groups.

On the following page, make sure Rule builder is selected as Rule type and switch to Rule JSON editor tab.

Fig 8. JSON Rule Builder

Overwrite the existing rule in the web ACL rule editor with the below JSON rule and click on the Validate button to make sure the syntax is correct. Once the rule is successfully validated, click on the Add rule button.

WAF Rule in JSON:

{
    "Name": "block-aws-domain",
    "Priority": 0,
    "Action": {
        "Block": {}
    },
    "VisibilityConfig": {
        "SampledRequestsEnabled": true,
        "CloudWatchMetricsEnabled": true,
        "MetricName": "block-aws-domain"
    },
    "Statement": {
        "OrStatement": {
            "Statements": [
                {
                    "ByteMatchStatement": {
                        "FieldToMatch": {
                            "SingleHeader": {
                                "Name": "host"
                            }
                        },
                        "PositionalConstraint": "ENDS_WITH",
                        "SearchString": "cloudfront.net",
                        "TextTransformations": [
                            {
                                "Type": "NONE",
                                "Priority": 0
                            }
                        ]
                    }
                },
                {
                    "ByteMatchStatement": {
                        "FieldToMatch": {
                            "SingleHeader": {
                                "Name": "host"
                            }
                        },
                        "PositionalConstraint": "ENDS_WITH",
                        "SearchString": "amazonaws.com",
                        "TextTransformations": [
                            {
                                "Type": "NONE",
                                "Priority": 0
                            }
                        ]
                    }
                }
            ]
        }
    }
}
Note: Feel free to modify to modify the checks in the above JSON rule as per your requirements. Eg: If you are planning to attach the WAF to a CloudFront distribution, you can remove the second rule but if you won't be attaching this WAF to a CloudFront distribution, you can delete the first rule.

Fig 9. Web ACL Rule Creation

On the next page, you will be asked to set the priority of the rule and in case you have multiple rules, make sure to move the block-aws-domain rule to the top.

Fig 10. Web ACL Rule Priority

Note: If you would have noticed we have set our rule action to Block if the condition matches because we have set the default action of the web ACL to allow the requests but in case, you have set to block the requests by default at web ACL level make sure to set the action to Allow for the rule and negate the statements.

Conclusion

Congratulations! Using the above WAF rule we are able to successfully block connections to default AWS domains and only serve requests created using the custom domain. However, this solution is not a one-stop solution for securing our workloads completely. We need to add rules to protect the application from bots, blacklisted IPs, SQL injection, cross-site scripting, bad inputs, Linux-specific Local File Inclusion (LFI) and many such other attacks. W can either use the managed rules provided by AWS, AWS marketplace sellers or write our own rules.


Covering the basics

  • WAF works on layer 7 of OSI model and hence, protects your application only against application layer attacks like SQL injections, cross-site scripting (XSS), cookie manipulation, CSRF and other type of OWASP attacks. Due to this, you should deploy security at different layers like deploy IDS/IPS. Moreover, it also adds latency to the overall processing of a request.

  • Primarily, there are three types of WAF: cloud, software and hardware based firewalls. A cloud-based WAF is managed by the cloud provider like AWS WAF and is affordable and easy to implement. A software-based WAF on the other hand needs to installed and configured by us either on an on-premise machine or on public or private cloud. Lastly, the hardware-based WAF also known as network-based WAF is a dedicated device installed locally on the network. This type of WAF requires deep expertise and is deployed when business critical applications deployed on-prem needs to be protected.

  • If planning on hosting a static website on S3, you should consider it serving via a CloudFront distribution because S3 website hosting does not support HTTPS. Moreover, CloudFront improves data delivery speed by caching data near to the user. Caching also reduces the cost because the content is not always fetched from the S3 bucket.

  • WAF is an application layer firewall that protects web applications against layer 7 attacks like cross-site scripting (XSS), SQL injection, session hijacking, cookie manipulation and other OWASP attacks based on request headers, body, query string, etc. whereas NACL is a component of VPC that can only allow or block a request based on a port number and IP address

Vimal Paliwal

Vim is a DevSecOps Practitioner with over eight years of professional experience. Over the years, he has architected and implemented full fledged solutions for clients using AWS, K8s, Terraform, Python, Shell, Prometheus, etc keeping security as an utmost priority. Along with this, during his journey as an AWS Authorised Instructor he has trained thousands of professionals ranging from startups to fortune companies.

Previous
Previous

Securing authentication between Terraform Cloud and AWS using OIDC

Next
Next

Disaster Recovery for AWS CloudHSM