Taking good security measures to protect your web applications has always been extremely important. You do not want to be that guy (you know, that guy) that gets to explain to his boss why some random hacker managed to exploit your unprotected application API.
There are several ways to protect yourself from such vulnerabilities, and in the end it all starts with writing clean and secure code, but… unfortunately we’ve all seen the pieces of code that occasionally get written on Friday afternoon moments when your brains feel more like the contents of a plate of cheddar cheese with Nachos that’s been in the oven for a bit too long. Fortunately you can take some complementary security measures to reduce, mitigate or even block the risk of someone exploiting vulnerable code by adding some sort of web application firewall.
If your website’s hosted by one of the major Cloud computing providers like AWS, GCP or Azure you can consider yourself lucky as they all have services that can be activated with only a few clicks (or CLI commands 👌). For those that have a website that is not hosted on a major cloud provider you can also consider yourself lucky because I will show you how you can secure your remotely hosted web application with an AWS WAF - Web Application Firewall on Amazon Web Services.
TLDR;
Set up a new AWS WAF instance, and a new Cloudfront distribution, which connects to the WAF instance. On the Cloudfront distribution, configure your remote web application server as origin and make sure the Host header is passed along each request. Finally, set a random “security” header to be sent along with each request so you can easily configure the origin to allow connections through this WAF.
Amazon WAF setup
Login to your Amazon Console and navigate to “WAF & Shield”. Create a new web ACL and as “Resource type” you have to select “CloudFront distributions”.
When that’s done, you can continue with the setup. If you run into any issues, have a look at the “Getting Started with AWS WAF documentation”.
Amazon CloudFront distribution setup
Amazon CloudFront is a fast content delivery network (CDN) service that securely delivers data, videos, applications, and APIs to customers globally with low latency, high transfer speeds, all within a developer-friendly environment. — AWS Cloudfront
Each CloudFront distribution contains an origin, the place where the actual files are hosted. Since AWS does not enforce the origin to be on the AWS network I will show you have you can configure the CloudFront distribution to serve as some kind of reverse proxy (with WAF firewall protection…).
- Create a new DNS record (A or CNAME) “origin.example.com” which points to the server where your web application is hosted.
- Via the AWS Certificate Manager create the SSL certificates for the *.example.com and example.com
- In your Amazon Console navigate to “CloudFront” and create a new distribution:
- Origin Domain Name: origin.example.com
- Minimum Origin SSL Protocol: select the latest protocol that your origin server supports
- Origin Protocol Policy: Match Viewer (make sure you have SSL working correctly on your origin server)
- AWS WAF Web ACL: Select the WAF you created in the previous step
- Alternate Domain Names: example.com and *.example.com (or others…)
- SSL Certificate: Custom SSL Certificate and select the SSL certificate you created in step 2
- Enable IPv6: Disable/Enable, up to you
- When the distribution has been created, head over to the “Behaviors” tab, select the behavior and edit it
- Viewer Protocol Policy: HTTP and HTTPS
- Cache and origin request settings: Use legacy cache settings
- Whitelist Headers: Host
- Object Caching: Customize
- Minimum TTL: 0
- Maximum TTL: 0
- Default TTL: 0
- Forward Cookies: All
- Query String Forwarding and Caching: Forward all, cache based on all
Hit the save button and wait until your distribution finishes updating. Now it’s time to test whether this “basic” works before we add some additional security.
Testing your setup
For testing your setup we will first manually edit the hosts file on your computer to see if everything works as expected. Add this to your hosts file:
12.34.56.78 example.com a.example.com b.example.com
First do a check via CURL to see whether the request passes through CloudFront:
~ curl -I https://a.example.com/
HTTP/2 200
content-type: text/html; charset=UTF-8
date: Fri, 16 Oct 2020 13:35:03 GMT
server: Apache
vary: Accept-Encoding
x-cache: Miss from cloudfront
via: 1.1 120b3c0f2e15a536c71cef7658e3ea14.cloudfront.net (CloudFront)
x-amz-cf-pop: BRU50-C1
x-amz-cf-id: e8I3it5WDhH91aVyr58-iq-Ay2PUKPURjWvfHzu6mTnmJUrGHYm-iw==
If you see the response contains the via: 1.1 ... (CloudFront)
it means that everything is working as expected. You can now launch your browser and navigate to a.example.com. The request should pass through your CloudFront Distribution (and firewall) before it goes to your origin’s server. 🤜 🤛
When you feel comfortable that everything works, now is the time to remove the line from your hosts file and update them in your DNS management tool.
Only allow requests through the AWS WAF
When you have everything up-and-running it’s time to configure to block all requests that don’t go through the AWS WAF firewall. The idea behind this is really simple:
- You update your ClodFront Distribution to make sure it sends along a customer header with each request
- Go to the “Origins and Origin Groups” tab
- Select and Edit the Origin
- Add an entry under “Origin Custom Headers”, for example:
This means that every request will not contain the HTTP_SECUREKEY header with the value you configured. On the server of your origin you can now check the presence of this header and whether it’s value matches the one that’s defined.
In nginx you could handle it this way:
set securekey $http_SECUREKEY;
if ($securekey != 'M3uzfFkowMPnyeuEDNMdtyvPGNUzUZ') {
return 301 https://www.youtube.com/watch?v=dQw4w9WgXcQ;
}
Conclusion
You should now have a web application which is accessible through CloudFront and is protected by an Amazon WAF firewall instance. When I first started working with CloudFront Distributions I was only using it as a CDN solution for serving static files but as you can see it can also serve in different use cases. 🎉
Do you think this is a good solution you? Are there things you would solve a different way? Let me know ⬇️