DEV Community

Cover image for Block direct access to CloudFront origins with custom headers and AWS WAF
Tanushree Aggarwal
Tanushree Aggarwal

Posted on

Block direct access to CloudFront origins with custom headers and AWS WAF

Introduction:

In today's age, insecure web applications are prime targets for cybercriminals seeking unauthorized access to sensitive information. Such breaches can lead to the exposure of personal, financial, or proprietary data, resulting in legal liabilities and loss of customer trust. A compromised web application can severely damage an organization's reputation. Broken Access Control occurs when users can access resources beyond their authorization, leading to unauthorized data exposure, and this type of security vulnerability is more common than you may think!
In this blog we will walkthrough a web architecture, which at first glance will probably not indicate the underlying issue to most readers.
Stay tuned!

Services:

Amazon Web Application Firewall
AWS WAF is a web application firewall that lets you monitor the HTTP(S) requests that are forwarded to your protected web application resources. AWS WAF lets you control access to your content. Based on criteria that you specify, such as the IP addresses that requests originate from or the values of query strings, the service associated with your protected resource responds to requests either with the requested content, with an HTTP 403 status code (Forbidden), or with a custom response.1

Current Architecture:

Image description

  • We have a Amazon Virtual Private Cloud(VPC), spanning two Availability Zones(AZ). Each AZ has a public and a private subnet.
  • The private subnets host our Elastic Compute Cloud (EC2) instances, which act as our web server virtual machines(VMs), configured with an Auto Scaling group(ASG).
  • The public subnets have NAT gateways for the outbound traffic from our VMs.
  • Inbound traffic is routed to our VMs through an Application Load Balancer(ALB). The Security Groups(SGs) attached to our EC2 instances allow traffic only from the SG attached to the ALB. This means that the EC2 instances will only receive inbound traffic from our ALB, all other inbound traffic will be denied.
  • An Amazon CloudFront distribution is sending traffic to our ALB. This is the first point of entry for the internet traffic.
  • The ALB SG is configured to allow traffic only from the CloudFront prefixes.
  • Our CloudFront distribution is protected by Amazon Web Application Firewall(WAF), which blocks all commonly known security vulnerabilities.

Problem

How would you rate the above architecture?

Are you able to spot the security flaw?

No?

Need a hint?

The issue lies somewhere here:

Image description

If your answer is still No, do not worry! A vast majority of people probably won't be able to spot the security flaw!

So what exactly is the issue here?

Let me demonstrate.

We already have a CloudFront distribution sending traffic to our ALB.

What happens if I create another CloudFront distribution having the same Application Load Balancer as Origin?

Let's check!

Image description

Yes! The traffic goes through!

But why?

Understand that incoming traffic to our Application load balancer is allowed for all the CloudFront prefixes.

Image description

This is because CloudFront does not have one single I.P address, rather a range of I.P addresses, and those too change frequently.
To make this management easier, AWS introduced a prefix list which we have configured as the source in our ALB Security Group.

This is the problem!

We want only our CloudFront distribution to be able to send traffic to our ALB, and NO other.

So how do we fix this?

Good news - This can be achieved by making two small changes to our existing configuration!

Proposed Architecture

Image description

1) Our ALB should be able to identify which CloudFront distribution is the correct one - for this we will update our CloudFront distribution to send some additional custom headers while sending the request to the ALB.

2) Traffic from all other CloudFront distributions (which can be within our account or some alien account) to be blocked - for this we will add a Web Application Firewall rule, with the default action as block, and only allow traffic after checking the request headers!

Cost Warning:

Proceed with caution!
AWS WAF is not covered under the AWS free-tier.
Charges are based on the following factors:

  • Number of web ACLs created.
  • Number of rules configured for each web ACL.
  • Number of web requests received.

Refer WAF pricing details here

Implementation:

Step-1:

Let us update our CloudFront distribution to send custom headers.

Navigate to your distribution, and under the Origins tab, select the ALB, and click Edit.

Image description

Scroll down to reach the Add custom headers section, and click Add header

Image description

Here, we can add any custom HTTP header of our choice. Refer the list before making your choice. I am selecting x-origin-verify for the demo, it can be anything else, the possibilities are endless, you can choose one as per your architecture (eg: authentication, source i.p address etc.)

Image description

Add the header and save. The distribution will take a few minutes to update.

Step-2:
Let's create the WAF rule.

Navigate to WAF & Shield from the hamburger menu.

Image description

Click Create web ACL

Select Regional Resources and the region in which your Application Load Balancer is based. Also give the web ACL a name. Click Next.

Image description

For the Associated AWS resources, we will add our ALB. Click Add AWS resources.

Image description

Select the Application Load Balancer radio button. If you created the rule in the same region as the ALB, you should be able to see your ALB name populate in the Resources section. Select the relevant ALB from the list.

Image description

At the next screen, from the Add rules dropdown, select My own rules and rule groups.

Image description

Select Rule builder.
Give the WAF rule a meaningful name, and select type as Regular rule

Image description

Let's create our custom rule.
First we select matches the statement, and then base our scenario on it.

From the Inspect dropdown select Single Header and for the Header field type x-origin-verify, because we want to allow traffic only for the web request with the header we configured in our CloudFront distribution.

Next, select Exactly matches string for the Match type.

String to match will be the string we are passing as custom header from our CloudFront distribution originsTest.

We can optionally also perform some Text Transformation, which is not in scope of this blog.

Image description

We continue defining our rule.
From the Action dropdown select the action that will be performed if the criteria we defined above is met. Allow.
Click Add rule.

Image description

A recently added feature show us how many capacity units will be used by the defined rule.

Select the Default action which will be performed for all requests that do not match our criteria
We choose Block

Image description

At the next screen, select your rule and click Next.

Image description

You can choose to enable CloudWatch metrics to view the rule in action.

Image description

Review your configuration and click Create. This may take a few minutes to complete, post which a Successful message will pop up on the screen.

Image description

Our WAF rule is active now!
Let's put it to the test!

Try accessing the application from your original CloudFront distribution.
Does the traffic go through?
Yes! It does!

Now try accessing the CloudFront distribution we created without the custom headers.
We are no longer able to access the web application through this distribution, meaning that our WAF ACL is working perfectly!

Image description

As the last step of our verification, let's go back to our WAF ACL, and check the Sampled request tab.
All the requests that were inspected by this WAF ACL will be displayed here, along with the associated action.

Image description

Image description

Conclusion:

In this blog we walked through a very common web architecture and uncovered the underlying security flaw. We then proceeded to make a minor tweak in the existing flow which changed our security game altogether! The current state is not the perfect architecture and can be modified in a number of ways to improve our security posture even further.
Amazon WAF offers many other features which were not in scope of this blog.

References:

  1. Amazon WAF pricing announcement
  2. Custom headers to origin requests
  3. Updating CloudFront distributions

  1. https://docs.aws.amazon.com/waf/latest/developerguide/waf-chapter.html 

Top comments (1)

Collapse
 
leob profile image
leob

Very thorough write-up, complex stuff but explained very clearly!

WAF really isn't free, not at all - good that you pointed that out ...

I mean, just define or use a couple of rules, and voila - right away you have a monthly bill of 15, 20, 30 dollars, even when you're serving zero requests ... which might not sound like a huge amount (especially not in the 'corporate' world), but it's the same or more as most "services" for which I have a subscription and which I feel are doing more ... WAF does "just one thing" (even when it does it well).

Makes me think I'd only introduce WAF when I experience first hand that there's an actual need for it, not "just because I can".

As a first line of defense, AWS budget alerts are your friend, and then maybe throttling offered by your backend application framework.