Posts Configure HTTP Security headers with CloudFront Functions
Post
Cancel

Configure HTTP Security headers with CloudFront Functions

A couple of weeks ago, AWS released CloudFront Functions — a “true edge” compute capability for the CloudFront.

It is “true edge” because Functions work on 200+ edge locations (link to doc) while its predecessor, the [email protected], runs on a small number of regional edge caches.

One of the use cases for [email protected] was adding security HTTP headers (it’s even listed on the product page), and now there is one more way to make it using CloudFront Functions.

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:

You can find a short and detailed explanation for each security header on Web Security cheatsheet made by Mozilla

CloudFront Functions overview

In a nutshell, CloudFront Functions allow performing simple actions against HTTP(s) request (from the client) and response (from the CloudFront cache at the edge). Functions take less than one millisecond to execute, support JavaScript (ECMAScript 5.1 compliant), and cost $0.10 per 1 million invocations.

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.

Solution overview

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:

Create Function

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.

Build Function

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
function handler(event) {
var response = event.response;
var headers = response.headers;

headers['strict-transport-security'] = { value: 'max-age=63072000; includeSubdomains; preload'}; 
headers['content-security-policy'] = { value: "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'; frame-ancestors 'none'"}; 
headers['x-content-type-options'] = { value: 'nosniff'}; 
headers['x-xss-protection'] = {value: '1; mode=block'};
headers['referrer-policy'] = {value: 'same-origin'};
headers['x-frame-options'] = {value: 'DENY'};

return response;
}

Test 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.

Publish Function

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
> curl -i https://d30i87a4ss9ifz.cloudfront.net
HTTP/2 200
content-type: text/html
content-length: 140
date: Sat, 22 May 2021 00:22:18 GMT
last-modified: Tue, 27 Apr 2021 23:07:14 GMT
etag: "a855a3189f8223db53df8a0ca362dd62"
accept-ranges: bytes
server: AmazonS3
via: 1.1 50f21cb925e6471490e080147e252d7d.cloudfront.net (CloudFront)
content-security-policy: default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'; frame-ancestors 'none'
strict-transport-security: max-age=63072000; includeSubdomains; preload
x-xss-protection: 1; mode=block
x-frame-options: DENY
referrer-policy: same-origin
x-content-type-options: nosniff
x-cache: Miss from cloudfront
x-amz-cf-pop: WAW50-C1
x-amz-cf-id: ud3qH8rLs7QmbhUZ-DeupGwFhWLpKDSD59vr7uWC65Hui5m2U8o2mw==

You can also test your results here — Mozilla Observatory

Read more

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:

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:

Liked the article? Please share it in the social media to support me!
Have a question? Feel free to contact me anytime!

This post is licensed under CC BY 4.0 by the author.