DEV Community

Jurriaan Proos
Jurriaan Proos

Posted on • Originally published at jurriaan.cloud on

Improve your site’s security using Lambda@Edge

In this post I’ll show you how I improved my site’s security using Lambda@Edge.

I recently went through my list of bookmarks again and stumbled upon https://securityheaders.com/, a website that scans a URL and gives it a rating based on the security headers that are set in the response.

So I gave it a try with my personal website:

Bad result from securityheaders.com

Oops.

Let’s improve that.

In a previous post I already mentioned that I’m serving my blog using CloudFront and S3, which means I can easily add another Lambda@Edge function that add the required security headers to get this rating up.

The AWS documentation already gives quite a complete example on how to do this in Node.js, so I ported that to Python as that is what I use for the other Lambda@Edge function:

def handle_event(event, context):
    response = event["Records"][0]["cf"]["response"]
    headers = response["headers"]

    headers["strict-transport-security"] = [{
        "key": "Strict-Transport-Security",
        "value": "max-age=63072000; includeSubdomains; preload"
    }]
    headers["content-security-policy"] = [{
        "key": "Content-Security-Policy",
        "value": "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"
    }]
    headers["x-content-type-options"] = [{
        "key": "X-Content-Type-Options",
        "value": "nosniff"
    }]
    headers["x-frame-options"] = [{
        "key": "X-Frame-Options",
        "value": "DENY"
    }]
    headers["x-xss-protection"] = [{
        "key": "X-XSS-Protection",
        "value": "1; mode=block"
    }]
    headers["referrer-policy"] = [{
        "key": "Referrer-Policy",
        "value": "no-referrer-when-downgrade"
    }]

    # If you remove the Server header in your code, CloudFront will still add its own header but it does not leak 
    # information about the backend server and does not give an attacker new information because they already know that 
    # they are connecting to a CloudFront IP address. <https://stackoverflow.com/a/58609507>
    if "server" in headers:
        del headers["server"]

    return response
Enter fullscreen mode Exit fullscreen mode

I then configured it to trigger on the origin-response event in my serverless.yaml (see the previous post for the rest of the serverless.yaml):

functions:
  originResponse:
    handler: src/functions/origin_response/handler.handle_event
    events:
      - cloudFront:
          eventType: origin-response
          origin: http://${self:custom.domainName}.s3-website-eu-west-1.amazonaws.com
Enter fullscreen mode Exit fullscreen mode

and deployed it.

After invalidating the cache I ran the check again:

Somewhat better result from securityheaders.com

Ok, getting somewhere!

I then fixed the error in the Strict-Transport-Security header (there’s a space in the value field in the example AWS gives) and added the following Permissions-Policy header.

headers["permissions-policy"] = [{
    "key": "Permissions-Policy",
    "value": "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), "
             "payment=(), usb=()"
}]
Enter fullscreen mode Exit fullscreen mode

Again invalidated the cache and ran the test again:

Best result from securityheaders.com

🎉

That’s it, in about 30 minutes I went from an F to an A+ rating!

The creator of https://securityheaders.com/ has written about all the headers above, so in case you’d like to learn more about those I suggest you check out these posts:

Other useful links

Top comments (0)