- Simple AWS
- Posts
- Using AWS WAF to Improve Application Security
Using AWS WAF to Improve Application Security
Setting up AWS Web Application Firewall to protect from Cross-Site Scripting, OS command execution and SQL injections, without modifying the application code.
You've deployed the most modern and sophisticated insecure web application: OWASP Juice Shop. It's an app made intentionally insecure, and it contains a vast number of hacking challenges of varying difficulty where the user is supposed to exploit the underlying vulnerabilities.
We're not going to hack it though, we're going to secure it! (at least partially). However, we won't touch the application's code.
Services
CloudFront: A Content Delivery Network (CDN) that caches content in edge locations distributed around the world and serves it to users with less latency than sending a request to your servers.
Web Application Firewall (WAF): A web application firewall that lets you monitor HTTP and HTTPS requests sent to your web resources, and take actions based on certain parameters. It integrates with CloudFront.
Solution: Set up Web Application Firewall (WAF) to block requests with exploits
To follow along the steps of this article, you'll first need to deploy the initial setup. You can find the code here, and you can deploy it to AWS with the following button:
Step 0: Test the app
First we'll make sure the exploits actually work! If your code already protects you from these, it's still a good idea to add these WAF rules, so you have another layer of protection (defense in depth!). But if the exploits didn't work on this tutorial, it would be a really bad tutorial, wouldn't it?
In the AWS console, open CloudShell
Set the URL of your CloudFront distribution as an env var
First, run
echo $JUICESHOP_URL
to see if it's already set up. If it is, that's it.If it's not set up, go to the initial stack you deployed in CloudFormation, and from the Outputs tab copy the value for JuiceShopURL
Run
JUICESHOP_URL=[the value you just copied]
, replacing [the value you just copied] with the value you just copied, which looks something like http://d1486tu9ui8f00.cloudfront.net
Run the following command, which sends a request where query arguments contain system file extensions that are unsafe to read or run:
curl -I $JUICESHOP_URL?execute=http://evilhackerz.com/file.ini
Verify that the response is 200 OK
Run the following command, which sends a request with a Cross-Site Scripting attack:
curl -X POST $JUICESHOP_URL -F "user=''"
Verify that the response is 200 OK (it should be an HTML with <title>OWASP Juice Shop</title>)
Run the following command, which attempts an SQL injection:
curl -X POST $JUICESHOP_URL -F "user='AND 1=1;"
Verify that the response is 200 OK (it should be an HTML with <title>OWASP Juice Shop</title>)
Run the following command, which attempts a more complex SQL injection:
curl -X POST $JUICESHOP_URL -F "1094 and 3=substirng(version(),1,1)"
Verify that the response is 200 OK (it should be an HTML with <title>OWASP Juice Shop</title>)
Step 1: Create a WAF Web ACL
A Web ACL doesn't actually protect anything by itself. It's the engine that runs everything that WAF does, and it's the core resource of WAF. In this step we're just creating it, and in the next steps we'll add the actual protections: the rules.
Go to the AWS WAF Console
Click Create web ACL
For name and description, enter simple-aws-waf
Set the Resource type as Amazon CloudFront distributions
Under Associated AWS resources, click Add AWS resources
Choose the CloudFront distribution that was created by the initial setup, and click Add
Click Next
Click Next
On the Configure metrics page, under Request sampling options, select Enable sampled requests
Click Next
Click create web ACL
Step 2: Enable WAF Logging
If a tree falls in a forest and no one is around to hear it, does it make a sound? It actually does, because of conservation of energy. But in this case we want to know what WAF is doing, that's what the logs are for.
Click the web ACL you just created and open the Logging and metrics tab
In the Logging section, click Enable
For Logging destination, make sure CloudWatch Logs log group is selected
For the log group, click Create new
For the log group name, enter aws-waf-logs-simple-aws-waf
Leave the other settings at default and click Create
Go back to the browser tab with the WAF console, refresh the log group list and select the log group you just created
Click Save
In the Sampled requests section, click Edit
Select Enable sampled requests
Click Save
Step 3: Add AWS managed rules groups to the web ACL
This is where we actually protect our app! Rule groups are sets of rules that WAF will evaluate for every request. Rules have statements such as "If the request contains header X" and actions that can be Allow, Block, Count or CAPTCHA and Challenge. Requests are evaluated against all rule statements to see if they match, and if there's a match the rule action is applied. Rules are grouped in Rule groups, to make them more manageable.
Managed rule groups are rule groups already created by AWS, with rules that will protect you from some common exploits such as Cross-Site Scripting (XSS). In this step we're enabling a couple of them, that will protect us from the first 2 exploits.
Go to the WAF console and select your web ACL
Click the Rules tab
In the Rules section, click Add rules, then click Add managed rule groups
Expand the listing for the AWS managed rule groups
In the Action column, turn on the Add to web ACL toggle for Core Rule Set and SQL Database
Click Add rules, then click Save
Step 4: Test the rules!
We have our rules in place, now it's time to test them. Let's re-run the first two exploits, and see if the requests are blocked by WAF.
In the AWS console, open CloudShell
Set the URL of your CloudFront distribution as an env var
First, run
echo $JUICESHOP_URL
to see if it's already set up. If it is, that's it.If it's not set up, go to the initial stack you deployed in CloudFormation, and from the Outputs tab copy the value for JuiceShopURL
Run
JUICESHOP_URL=[the value you just copied]
, replacing [the value you just copied] with the value you just copied, which looks something like http://d1486tu9ui8f00.cloudfront.net
Run the following command, which sends a request where query arguments contain system file extensions that are unsafe to read or run:
curl -I $JUICESHOP_URL?execute=http://evilhackerz.com/file.ini
Verify that the response is 403 Forbidden
Run the following command, which sends a request with a Cross-Site Scripting attack:
curl -X POST $JUICESHOP_URL -F "user=''"
Verify that the response is 403 Forbidden (it should be an HTML with <title>ERROR: The request could not be satisfied</title>)
Step 5: Add a rule to block SQL injections
In this step we're adding a custom rule that will protect us from SQL injections. We're not actually writing the statement, and instead using a match already predefined by AWS: Contains SQL injection attacks. We are choosing all the other options however.
Go to the WAF console and select your web ACL
Click the Rules tab
In the Rules section, click Add rules and click Add my own rules and rule groups
For the rule name, enter block-sql-injection
In the If a request dropdown, make sure matches the statement is selected
Under Statement:
For Inspect, select Body
For Match type, select Contains SQL injection attacks (it's a searchable field)
For Oversize handling, select Continue
For Sensitivity level, select High
Click Add Rule
Click Save
Step 6: Test the SQL injection rule
With the new rule in place, we re-run the last two exploits, which were SQL injections of different complexity. They should both be blocked.
In the AWS console, open CloudShell
Set the URL of your CloudFront distribution as an env var
First, run
echo $JUICESHOP_URL
to see if it's already set up. If it is, that's it.If it's not set up, go to the initial stack you deployed in CloudFormation, and from the Outputs tab copy the value for JuiceShopURL
Run
JUICESHOP_URL=[the value you just copied]
, replacing [the value you just copied] with the value you just copied, which looks something like http://d1486tu9ui8f00.cloudfront.net
Run the following command, which attempts an SQL injection:
curl -X POST $JUICESHOP_URL -F "user='AND 1=1;"
Verify that the response is 403 ERROR (it should be an HTML with <title>ERROR: The request could not be satisfied</title>)
Run the following command, which attempts a more complex SQL injection:
curl -X POST $JUICESHOP_URL -F "1094 and 3=substirng(version(),1,1)"
Verify that the response is 403 ERROR (it should be an HTML with <title>ERROR: The request could not be satisfied</title>)
In the previous step I told you to select High for Sensitivity level. If you had selected Low, the first of these two exploits (a really simple SQL injection) would have been blocked, but the last one (a more complex SQL injection) wouldn't have been caught. A High Sensitivity level can also cause false positives though, so test it for yourself.
Step 7: View the WAF logs in CloudWatch Logs
We've already seen WAF in action from the attacker's point of view. But in a real scenario you're probably not going to be the one attacking. From your point of view, the information you have are the logs.
Go to the WAF console and select your web ACL
Click the CloudWatch Logs Insights tab
Click Run query
Under the Logs tab that appeared below, open the log entries and take a couple of seconds to read them
Briefly consider how much of a pain it would be to read these logs!
In the text area above the Run query button, delete everything and paste the following:
fields httpRequest.clientIp as ClientIP, httpRequest.country as Country, httpRequest.uri as URI, terminatingRuleId as Rule
| filter action = "BLOCK"
| stats count(*) as RequestCount by Country, ClientIP, URI, Rule
| sort RequestCount desc
Click Run query again
Now check the logs! Much better, right?
Bonus: You can do aggregate queries! Here's a list of useful queries that you can copy.
WAF Pricing
WAF's pricing is actually pretty complex, and you'll find 9 different examples in the pricing page. Here are the basics though:
As a base, you're charged $5/month per Web ACL
Plus $1/month per rule group (managed or created by you)
Plus $1/month per custom rule (either in a rule group or not)
Plus $0.60 per 1 million requests (up to 16KB body, up to 1500 WCUs)
Plus $0.30 per million requests for each additional 16KB
Plus $0.20 per million requests for each additional 500 WCUs
WCUs are Web ACL Capacity Units. When you create a rule, WAF calculates how many WCUs it consumes depending on the complexity of the rule. For rule groups, when you create them you set the WCU capacity (can't change it later), and then the sum of the WCUs of all the rules you add can't exceed that rule group's WCU capacity. When you assign rules and rule groups to a Web ACL, the WCUs consumed by the Web ACL is the sum of the WCUs of all rules associated directly, plus the sum of the WCU capacity of all rule groups associated with the Web ACL.
For reference, the 1500 WCUs included in the base $5/month price is usually a lot. The max WCUs a Web ACL can have is 5000 (can be increased by contacting support)
Regarding the body, you can decide what rules do when the body exceeds 16 KB. That's the Continue you set for Oversize handling in Step 5.
I wrote a more detailed article on WAF Pricing, which you might want to check out.
By the way, you can purchase WAF rule groups in the AWS Marketplace!
Best Practices for AWS WAF
Operational Excellence
Implement Continuous Monitoring and Alerting: Set up CloudWatch alarms to monitor WAF metrics, such as the number of blocked requests and rule matches. Configure automated notifications to get alerted when potential security threats are detected. This helps you proactively respond to and mitigate security risks.
Enable WAF Logging: We already did this =). We also analyzed the logs with CloudWatch Logs Insights, but you can also use Athena or Elasticsearch to better understand potential attack patterns and fine-tune the WAF rules accordingly.
Security
Regularly Update WAF Rules: Security is a race to patch the latest exploits. Stay up-to-date with the latest threats and update your WAF rules. Managed rule groups are automatically updated, but if you're a security pro, you probably want to stay on top of this.
Reliability
Test and Validate WAF Rules: Of the 7 steps we had in our step-by-step, 3 were testing! Set up the rules, then test them, then test them periodically, then learn about new threats and test them again!
Performance Efficiency
This time I've got nothing for you here.
Cost Optimization
Use Managed Rule Groups: You're billed $1/month per custom rule, and just $1/month per an entire managed rule group. Solve what you can with managed rule groups! Keep an eye on WCUs though.
AWS WAF Resources
The Guidelines for Implementing AWS WAF by AWS outlines recommendations for implementing AWS WAF to protect existing and new web applications. Great read before you mess up your app's security! And probably a great read if you've already messed it up.
Did you like this issue? |
Reply