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:
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
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
and deployed it.
After invalidating the cache I ran the check again:
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=()"
}]
Again invalidated the cache and ran the test again:
🎉
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:
- Hardening your HTTP response headers#Server
- HSTS - The missing link in Transport Layer Security
- Content Security Policy - An Introduction
- Hardening your HTTP response headers#X-Content-Type-Options
- Hardening your HTTP response headers#X-Frame-Options
- Content Security Policy - An Introduction
- A new security header: Referrer Policy
- Goodbye Feature Policy and hello Permissions Policy!
Top comments (0)