My experience getting an A+ from Mozilla’s Observatory tool on AWS

Cyrus Sarkosh
3 min readSep 15, 2019
The final architectural solution

Preface

This article is structured as a story; detailing my experience getting an A+ rating from Mozilla’s Observatory tool on AWS. It is not intended to be read as a tech doc. However, it tends to be technical in nature.

As well, my experience would have been made a lot easier had I hosted the site on an EC2 instance instead of the cdn-serverless architecture I implemented. However, this was my project. So I chose the architecture I saw as more fun: cdn-serverless.

Attempt 1: Route53, CloudFront, & S3

It started as a quick project that I could use to demo the quality of my web apps and use as a gimmicky portfolio site. I got a basic skeleton up and running: a Route53-CloudFront-S3 architecture hosting create-react-app using material-ui web components with automated deployments. Then, I began working on the most important part of any public project: the README.

My plan was to copy the look and feel off React’s README. I thought their README was short, direct, and had a professional look to it. So I modeled mine after it. Except, there was one thing I was missing, the cornerstone of all great READMEs: a collection of badges for bragging.

Browsing through what was available to me on shields.io, I began with adding build badges and an app health badge. Then, I came across the badge that is the reason for this whole blog post: Mozilla’s Observatory badge.

The Mozilla Observatory badge is a reflection of your site’s grade on Mozilla’s Observatory tool. The tool itself only checks that your site is served under HTTPS and has the recommended security headers set. It seemed simple enough, so I gave my site its first scan and got my first grade: F.

That sucked. The reason for the low grade was due to not having any of the security headers set. But, my site was still served and redirected-to HTTPS… so I got a 15/100. The next steps were obvious. I was going to simply add all the security headers as part of the CloudFront response. Problem was, CloudFront does not permit custom headers as part of its response.

Attempt 2: Route53, CloudFront, S3, Lambda@Edge

Amazon published an article detailing the work-around for this. Effectively, use a lambda running on CloudFront’s edge nodes to intercept the response just before it reached the client and set whatever headers I want. Easy-enough. I created a lambda function to add all the easy-to-implement headers (HSTS, CORS, X-Frame-Options, etc.), added the infrastructure to run it at all edge nodes, and gave Mozilla’s Observatory a scan: B.

Not bad. But, not what I wanted. Analyzing the Observatory’s results, I could have guessed why I missed the A grade: unsafe-eval specified in the script-src and style-src directives for the CSP header. The reason was that <style> and <script> tags were being injected at build time by create-react-app and at runtime by material-ui, and unsafe-eval allowed them to execute. I needed to a find way to safely allow create-react-app and material-ui’s dynamic <script> and <style> injections to execute.

cheerio (a.k.a. Node’s JQuery) to the rescue. Both script-src and style-src directives support a nonce source type; allowing inlined <script> and <style> tags to execute as long as the tag’s nonce attribute value matches the nonce value set for the CSP header’s style-src and script-src directives. Using cheerio and Node’s crypto library, I could generate a unique nonce per request and set it on each <style> and <script> tags prior to returning the webpage. Problem was: Lambda@Edge does not support modifying the response body within its response hooks.

Attempt 3: Route53, CloudFront, S3, API Gateway, Lambda

I needed a server. So, I created a lambda to serve and set nonces for the <script> and <style> tags. Then, I setup an API Gateway route to forward my lambda and added API Gateway as an origin in CloudFront; specifying that CloudFront forward root requests to API Gateway instead of S3.

I gave the Observatory tool another re-scan: A+.

--

--

Cyrus Sarkosh

Cloud Developer · Web Developer · Game Building Hobbyist