A couple of weeks ago, AWS released CloudFront Functions — a “true edge” compute capability for the CloudFront.
What are security headers, and why it matters
Security Headers are one of the web security pillars.
They specify security-related information of communication between a web application (i.e., website) and a client (i.e., browser) and protect the web app from different types of attacks. Also, HIPAA and PCI, and other security standard certifications generally include these headers in their rankings.
We will use CloudFront Functions to set the following headers:
- Content Security Policy
- Strict Transport Security
- Referrer Policy
You can find a short and detailed explanation for each security header on Web Security cheatsheet made by Mozilla
CloudFront Functions overview
Every CloudFront distribution has one (default) or more Cache behaviors, and Functions can be associated with these behaviors to execute upon a specific event.
That is how the request flow looks like in general, and here is where CloudFront Functions execution happens:
CloudFront Functions support Viewer Request (after CloudFront receives a request from a client) and Viewer Response (before CloudFront forwards the response to the client) events.
You can read more about the events types and their properties here — CloudFront Events That Can Trigger a Lambda Function - Amazon CloudFront.
Also, the CloudFront Functions allow you to manage and operate the code and lifecycle of the functions directly from the CloudFront web interface.
CloudFront distribution should exist before Function creation so you could associate the Function with the distribution.
Creation and configuration of the CloudFront Function consist of the following steps:
In the AWS Console, open CloudFront service and lick on the Functions on the left navigation bar, then click Create function button. Enter the name of your Function (e.g., “security-headers”) and click Continue.
On the function settings page, you will see four tabs with the four lifecycle steps: Build, Test, Publish, Associate.
Paste the function code into the editor and click “Save.”
Here is the source code of the function:
Open the “Test” tab — let’s try our function first before it becomes live!
Select Viewer Response event type and Development Stage, then select “Viewer response with headers” as a Sample test event (you will get a simple set of headers automatically).
Now click the blue “Test” button and observe the output results:
- Compute utilization represents the relative amount of time (on a scale between 0 and 100) your function took to run
- Check the Response headers tab and take a look at how the function added custom headers.
Let’s publish our function. To do that, open the Publish tab and click on the blue button “Publish and update.”
Associate your Function with CloudFront distribution
Now, you can associate the function with the CloudFront distribution.
To do so, open the Associate tab, select the distribution and event type (Viewer Response), and select the Cache behavior of your distribution which you want to use for the association.
Once you associate the function with the CloudFront distribution, you can test it in live mode.
I will use curl here to demonstrate it:
You can also test your results here — Mozilla Observatory
That was a simplified overview of the CloudFront Functions capabilities.
But if you want to get deeper, here is a couple of useful links to start:
- Another overview from AWS — CloudFront Functions Launch Blog
- More about creating, testing, updating and publishing of CloudFront Functions — Managing functions in CloudFront Functions - Amazon CloudFront
So what to choose?
CloudFront Functions are simpler than [email protected] and run faster with minimal latency and minimal time penalty for your web clients.
[email protected] takes more time to invoke, but it can run upon Origin Response event so that CloudFront can cache the processed response (including headers) and return it faster afterward.
But again, the CloudFront Functions invocations are much cheaper (6x times) than [email protected], and you do not pay for the function execution duration.
The final decision would also depend on the dynamic/static nature of the content you have at your origin.
To make a wise and deliberate decision, try to analyze your use case using these two documentation articles: