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:
- 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:
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!
Yes! The traffic goes through!
But why?
Understand that incoming traffic to our Application load balancer is allowed for all the CloudFront prefixes.
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
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
.
Scroll down to reach the Add custom headers
section, and click Add header
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.)
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.
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
.
For the Associated AWS resources
, we will add our ALB. Click Add AWS resources
.
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.
At the next screen, from the Add rules
dropdown, select My own rules and rule groups
.
Select Rule builder
.
Give the WAF rule a meaningful name, and select type as Regular rule
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.
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
.
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
At the next screen, select your rule and click Next
.
You can choose to enable CloudWatch metrics to view the rule in action.
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.
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!
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.
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.
Top comments (1)
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.