Low-Hanging Fruits of Web Security
Tags: webdev, security, nextjs, firebase
Published: 2021-02-12 06:35:09

> I talk about implementing common headers in your web app to increase its security.

### Headers

1. `Access-Control-Allow-Origin`: Defines domains that can access your content. [MDN link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin).

2. `Permissions-Policy`: Defines permissions your web app can request for. [W3C link](https://w3c.github.io/webappsec-permissions-policy/#permissions-policy-http-header-field).

3. `Referrer-Policy`: Defines how much information you share with link B about link A when you go from link A to link B. [MDN link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy).

4. `Strict-Transport-Security`: Defines whether or not `https://` should be compulsory when accessing your web app. [MDN link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security).

5. `X-Content-Type-Options`: Mandates content to be of the type defined. [MDN link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options).

6. `X-Frame-Options`: Defines whether or not your web app can be loaded within an iframe or other embed elements. [MDN link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options).

7. `X-XSS-Protection`: Defines Cross-Site Scripting filter level. [MDN link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection).

### Examples

Say you have a web app hosted on https://example.com:

```typescript
const HEADERS = [
  {
    key: 'Access-Control-Allow-Origin',
    value: 'https://example.com'
  },
  {
    key: 'Permissions-Policy',
    value: 'autoplay=(), camera=(), fullscreen=(), geolocation=(), microphone=()'
  },
  {
    key: 'Referrer-Policy',
    value: 'no-referrer'
  },
  {
    key: 'Strict-Transport-Security',
    value: 'max-age=63072000; includeSubDomains; preload'
  },
  {
    key: 'X-Content-Type-Options',
    value: 'nosniff'
  },
  {
    key: 'X-Frame-Options',
    value: 'DENY'
  },
  {
    key: 'X-XSS-protection',
    value: '1; mode=block'
  }
]
```

On Next.js apps deployed on Vercel, you set the headers in `next.config.js` like so:

```javascript
// next.config.js

const headers = async () => [
  {
    source: '/(.*)',
    headers: // Paste HEADERS here
  }
];
module.exports = {
  headers
};
```

On SPAs deployed on Firebase Hosting, you set the headers in `firebase.json` like so:

```javascript
// firebase.json

{
  "hosting": {
    "headers": [
      {
        "source": "**",
        "headers": // Paste HEADERS here
      }
    ]
  }
}
```

You may want to change the value of `Access-Control-Allow-Origin` header to allow [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).

You may also want to change the value of `Permissions-Policy` header based on permissions you intend to ask for.

### CSP: The King of Security Headers

`Content-Security-Policy` ([MDN link](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)) is an extremely powerful header since it combines various policies, allowing you to define the security policy of your web app in a fine-grained fashion. It is difficult to get right in the first attempt, so it behoves to not be included in the “low-hanging fruits” list above — but you shouldn’t skip it. It guarantees visitors have a safe time on your web app.

To build the tightest CSP for your web app, it is recommended to start with locked-down policies and open them up as you proceed. Chrome is very descriptive about CSP violations so it is easy to know which policy to open-up next. It is essential to test all features of your web app to ensure there are no violations. Use a new Incognito window every time you make changes to any policy to ensure you aren’t working with cached results.

It is common to disable CSP during development to allow tools to work properly. You can use an environment variable to disable CSP during development. Here’s an example of a CSP header configuration of a Next.js app that uses Google Fonts (note the usage of environment variable `NEXT_PUBLIC_IS_PRODUCTION`):

```javascript
// next.config.js

const HEADERS = [
  ...,
  {
    key: 'Content-Security-Policy',
    value: process.env.NEXT_PUBLIC_IS_PRODUCTION === 'true' ? "connect-src 'self'; default-src 'self'; font-src https://fonts.gstatic.com; img-src 'self' data:; manifest-src 'self'; object-src 'none'; script-src 'self'; style-src 'self' https://fonts.googleapis.com 'unsafe-inline'; upgrade-insecure-requests;" : ''
  },
  ...
]
```

You can evaluate the strictness of your policy using Google’s [CSP Evaluator](https://csp-evaluator.withgoogle.com).

### Quantifying the Result

[Mozilla Observatory](https://observatory.mozilla.org) is to Web Security, what [Lighthouse](https://developers.google.com/web/tools/lighthouse) is to Web Performance. It rates the security of your web app and provides suggestions to improve that rating. It also integrates 3rd party scanners like [Security Headers](https://securityheaders.com) so you can have multiple opinions.

![Mozilla Observatory’s security score of Share By Cam](https://dev-to-uploads.s3.amazonaws.com/i/ubnif984uhr9ht2kkx3x.png "Mozilla Observatory’s security score of Share By Cam")

---

Was I able to help you increase your web app’s security score? Let me know on [my Twitter](https://twitter.com/karmasakshi/status/1359986487467606021). Cheers!