There are numerous measures you can take to improve security with Lambda@Edge. Today we are going to cover five ways via the use of security headers. Lambda@Edge provides you with the ability to customize headers after responses have left the origin.
NEW RESEARCH: LEARN HOW DECISION-MAKERS ARE PRIORITIZING DIGITAL INITIATIVES IN 2024.
X-XSS-Protection
The X-XSS-Protection header helps prevent a number of cross-site scripting (XSS) attacks in a handful of browsers. Mozilla notes:
X-XSS-Protection is a feature of Internet Explorer and Chrome that stops pages from loading when they detect reflected cross-site scripting (XSS) attacks. Although these protections are largely unnecessary in modern browsers when sites implement a strong Content Security Policy that disables the use of inline JavaScript (‘unsafe-inline’), they can still provide protections for users of older web browsers that don’t yet support CSP.
Directions
- Create a Lambda function with the following code:
exports.handler = (event, context, callback) => { const { response } = event.Records[0].cf; response.headers["x-xss-protection"] = [{ value: "1; mode=block" }]; return callback(null, response); };
- Set the Lambda function to run as the CloudFront origin response or viewer response
X-Content-Type-Options
The X-Content-Type-Options header helps prevent MIME type related XSS attacks. Mozilla notes:
X-Content-Type-Options is a header supported by Internet Explorer, Chrome and Firefox 50+ that tells it not to load scripts and stylesheets unless the server indicates the correct MIME type. Without this header, these browsers can incorrectly detect files as scripts and stylesheets, leading to XSS attacks. As such, all sites must set the X-Content-Type-Options header and the appropriate MIME types for files that they serve.
Directions
- Create a Lambda function with the following code:
exports.handler = (event, context, callback) => { const { response } = event.Records[0].cf; response.headers["x-content-type-options"] = [{ value: "nosniff" }]; return callback(null, response); };
- Set the Lambda function to run as the CloudFront origin response or viewer response
X-Frame-Options
The X-Frame-Options header helps prevent frame related XSS attacks. Mozilla notes:
X-Frame-Options is an HTTP header that allows sites control over how your site may be framed within an iframe. Clickjacking is a practical attack that allows malicious sites to trick users into clicking links on your site even though they may appear to not be on your site at all. As such, the use of the X-Frame-Options header is mandatory for all new websites, and all existing websites are expected to add support for X-Frame-Options as soon as possible.
Directions
- Create a Lambda function with the following code:
exports.handler = (event, context, callback) => { const { response } = event.Records[0].cf; response.headers["x-frame-options"] = [{ value: "DENY" }]; return callback(null, response); };
- Set the Lambda function to run as the CloudFront origin response or viewer response
Content-Security-Policy
The Content-Security-Policy header helps prevent the vast majority of XSS attacks. Mozilla notes:
Content Security Policy (CSP) is an HTTP header that allows site operators fine-grained control over where resources on their site can be loaded from. The use of this header is the best method to prevent cross-site scripting (XSS) vulnerabilities. Due to the difficulty in retrofitting CSP into existing websites, CSP is mandatory for all new websites and is strongly recommended for all existing high-risk sites.
One of the main benefits of the CSP comes from the disabling of inline Javascript. New websites can be built with this in mind, but fixing all of the inline Javascript on older websites may not be an option. As such, exercise caution when adding a CSP to existing websites.
Directions
- Create a Lambda function with the following code:
exports.handler = (event, context, callback) => { const { response } = event.Records[0].cf; response.headers["content-security-policy"] = [ { value: "default-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; block-all-mixed-content", }, ]; return callback(null, response); };
- Set the Lambda function to run as the CloudFront origin response or viewer response
Gotchas
One challenge that single page applications (SPAs) face if they implement server side rendering (SSR) is with inlining critical styles to the document head and inlining application state to the document body. To successfully resolve this challenge, you will need to calculate hashes for the script(s) and style(s) and put base64 encoded versions in the script-src and style-src sections of the content security policy. For example:
script-src 'self' 'sha256-1LWiqTBX9ooah9yxrjAPIapj/bpKu8E2gxHrAcrvXJQ='; style-src 'self' 'sha256-lEmNQfaoD6+gZL66vjr9syPi0uIaBbN/95dTug2Tb1g=';
Strict-Transport-Security
The Strict-Transport-Security header prevents insecure access to your website. Mozilla notes:
HTTP Strict Transport Security (HSTS) is an HTTP header that notifies user agents to only connect to a given site over HTTPS, even if the scheme chosen was HTTP. Browsers that have had HSTS set for a given site will transparently upgrade all requests to HTTPS. HSTS also tells the browser to treat TLS and certificate-related errors more strictly by disabling the ability for users to bypass the error page.
Directions
- Create a Lambda function with the following code:
exports.handler = (event, context, callback) => { const { response } = event.Records[0].cf; response.headers["strict-transport-security"] = [ { value: "max-age=31536000; includeSubDomains" }, ]; return callback(null, response); };
- Set the Lambda function to run as the CloudFront origin response or viewer response
Extra Credit
If you are confident that all apex, subdomains, and nested subdomains of your domain will be served securely, you can submit your domain to be included on the HSTS preload list.
Putting It All Together
Rather than creating five separate Lambda@Edge functions, you can combine them all into one function.
Directions
- Create a Lambda function with the following code:
exports.handler = (event, context, callback) => { const { response } = event.Records[0].cf; response.headers["x-xss-protection"] = [{ value: "1; mode=block" }]; response.headers["x-content-type-options"] = [{ value: "nosniff" }]; response.headers["x-frame-options"] = [{ value: "DENY" }]; response.headers["content-security-policy"] = [ { value: "default-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; block-all-mixed-content", }, ]; response.headers["strict-transport-security"] = [ { value: "max-age=31536000; includeSubDomains" }, ]; return callback(null, response); };
- Set the Lambda function to run as the CloudFront origin response or viewer response
Testing the Results
Mozilla has created a great tool for testing the presence and validity of the aforementioned security headers on your website. You must perform the following steps:
- Visit the Mozilla Observatory tool in your browser
- Type the domain name of your website (E.g.: www.example.com)
- Click the Scan Me button
In Summary
Security should be one of the top concerns when operating any web application. Fortunately, Lambda@Edge makes it easy to implement best security practices across the board on your websites. I challenge you to incorporate all of the aforementioned headers on all of your web applications and strive for an A on the Mozilla Observatory tool.
Lucas Still
Related Posts
-
9 Ways Agile Developers Can Improve Their Scrum Skills
With more than 10 years of experience in Scrum and Scrum-like teams, I finally decided…
-
9 Ways Agile Developers Can Improve Their Scrum Skills
With more than 10 years of experience in Scrum and Scrum-like teams, I finally decided…