HTTP Security Headers and how to set them in Next.js

ยท

8 min read

HTTP Security Headers and how to set them in Next.js

Security headers are a type of HTTP header there to help us describe our site so we can help protect it against various potential attacks.

In this article, I will introduce HTTP headers including various different security headers that you might want to use. I will also show you how to implement them in a Next.js application.

Let's go!


Content

  1. What is a security header
  2. Examples of security headers
  3. Adding headers to a Next.js app
  4. The end!

What is a security header

First, we need to ask the following question.

What is an HTTP header?

An HTTP (HyperText Transfer Protocol) header is simply something that we use to pass some extra information with HTTP requests and responses. Think of them as notes that give more context to the main document. Each header is simply a key-value pair separated by a colon.

When you visit a website, the server receives a request from the browser. This is a request for more information about the site you are visiting which is exchanged from the HTTP request headers. The server then responds and the response will include HTTP response headers.

Diagram of the HTTP request and response to and from the server

An example of an HTTP header is the 'Authorization' header. It simply authenticates the user agent (software that acts on behalf of the user) with a server.

Authorization: Bearer <Some token>

So what is an HTTP security header?

We use security headers to help describe our sites specifically to protect against various potential attacks. When a user navigates to the site the server will respond with response headers that provide the browser with information on how to handle things.

By adding security headers to the response of our pages, we can minimize the possibility of the following attacks happening to our application.

  • Cross-Site Scripting (XXS)
  • Clickjacking
  • Code injection

Examples of security headers

Let's take a look at some of the important security headers that you may want to implement.

If you're already familiar with security headers, skip ahead to setting them up in Next.js here - Adding security headers to a Next.js app

Content Security Policy

The Content Security Policy header allows you to set a policy for which domains are approved for executable scripts. It's like setting a policy for children in a video streaming service. You can specify what they can and cannot watch and it's the same idea here.

Sometimes your site will need to execute scripts that come from origins other than your own application. With this header, we can fine-tune exactly which domains should be considered safe or 'whitelisted' and which should not.

We can also control the kind of resources we load into the site in more detail. For example, we could restrict loading scripts to our own domain but allow fonts or images to be loaded from a specific outside source.

Let's take a look at a few examples.

To allow content from any domain as if the policy was not set ๐Ÿ‘‡

Content-Security-Policy: default-src *

Or set the own origin as the only trusted domain ๐Ÿ‘‡

Content-Security-Policy: default-src 'self'

You can also specify any other domain other than your own trusted domain ๐Ÿ‘‡

Content-Security-Policy: default-src 'https://someotherdomain.com'

We can fine-tune our content security policy to set separate policies for resources like images, fonts, styles, media, scripts, and more like this ๐Ÿ‘‡

Content-Security-Policy: default-src 'self'; font-src 'self' 'https://fonts.googleapis.com'; image-src *.somewhere.com; script-src 'self'

In the above policy, we set the following

  1. We set a default source for the content as our site's origin so this will be used for all resources unless otherwise specified.
  2. We allow fonts to be loaded from our own site or from google fonts
  3. Images can be trusted from anywhere that ends in .somewhere.com
  4. Scripts are only trusted from our own site

Permissions Policy (Previously 'Feature Policy')

The recently renamed Permissions Policy security header allows you to control which browser APIs are allowed to be used within your site document or any iframes located in the document.

Things like geolocation, camera, microphone, and many more can all be disabled if you know your site does not require them and hence can reduce the possibility of attacks coming through these avenues as a result.

Permissions-Policy: camera=(); battery=(self); geolocation=(); microphone=('https://somewhere.com')

In the above example, we set the policies of several browser features.

  1. Camera is empty which means we deny the use of video input devices.
  2. Battery status API is allowed within your own domain
  3. Geolocation is empty which means we deny its use
  4. Audio input devices are allowed for the origin stated

X-Frame-Options

The X-Frame-Options header controls whether or not your site can be loaded within a frame. Allowing this can be dangerous because you leave yourself open to clickjacking attacks.

Clickjacking is an attack that involves tricking the user into clicking something that they believe to be something else. Attackers may disguise elements and the user is unable to see what they are actually clicking on.

We can help protect ourselves against this attack by preventing our site from being loaded within frames.

There are two options for this header

X-Frame-Options: deny

The recommended option is to deny your site being loaded within frames which is shown above using the 'deny' option.

X-Frame-Options: SAMEORIGIN

The 'SAMEORIGIN' option allows the site to be loaded within a frame while serving the site is the same as the one in the frame.

X-Content-Type-Options

The X-Content-Type-Option header ensures that MIME (Multipurpose Internet Mail Extensions) types specified in Content-Type are adhered to and we avoid what is known as 'MIME type sniffing'. Browsers might try and determine the MIME type based on the response content instead of what is specified in the Content-Type header.

It only has one option and it prevents the browser from trying to change the Content-Type away from what was declared.

X-Content-Type-Options: nosniff

Referrer policy

With this header, we can control what information is sent when a user navigates from one origin to another. Consider a user that clicks a link on what we will call the original site. This link takes the user to a different domain. When this happens, some information is sent to the new domain. Information about where the user came from.

There are several different options we can set with this header.

Referrer-Policy: origin-when-cross-origin

The 'origin-when-cross-origin' option sends the path, origin, and query string with a same-origin request from equal protocol levels. An example of an equal protocol level would be from HTTPS to HTTPS.

If the request is cross-origin, only the origin is sent.

Referrer-Policy: strict-origin-when-cross-origin

The 'strict-origin-when-cross-origin' option is similar to the previous option. The path, origin, and query string are included with same-origin requests regardless of protocol level.

When making cross-origin requests, only the origin is sent as long as the protocol level is the same and omits the header otherwise.

Referrer-Policy: strict-origin

With 'strict-origin', only the origin is sent when the protocol level is equal and otherwise omits the header.

Referrer-Policy: no-referrer

The 'no-referrer' setting will omit the referrer header.

There are further options which you can check out here

Strict Transport Security

With the Strict Transport Security header, you can specify that your site should only be accessed using the HTTPS protocol. Here we can specify the time in seconds that the browser will remember this protocol.

Strict-Transport-Security: max-age=31536000;

You can also extend the instruction to all subdomains with an optional setting

Strict-Transport-Security: max-age=31536000; includeSubDomains

You may think that setting up a redirect from 'http://' to 'https://' is enough but attackers could still seize sensitive information. Setting up a strict transport security setting will make this attack far more difficult.

Adding security headers to a Next.js app

Now that we've had a look at some security headers, let's quickly implement them in a Next.js app. Also, feel free to explore some of the other security headers available.

In Next.js we can set security headers from a next.config.js file located at the root of your project.

// next.config.js
module.exports = {
  async headers() {
    return [

    ]
  },
};

We return an array of headers that we specify inside JavaScript objects. You can choose to apply headers that will be sent with every route request or on a route-by-route basis. Let's set some of the headers that we previously explored.

// next.config.js
module.exports = {
  async headers() {
    return [
        {
          source: '/(.*)',
          headers: [
            {
              key: 'Content-Security-Policy',
              value:
                "default-src 'self'; font-src 'self' 'https://fonts.googleapis.com'; img-src 'self' *.somewhere.com; script-src 'self'",
            },
            {
              key: 'X-Frame-Options',
              value: 'DENY',
            },
            {
              key: 'X-Content-Type-Options',
              value: 'nosniff',
            },
            {
              key: 'Referrer-Policy',
              value: 'origin-when-cross-origin',
            },
            {
              key: 'Permissions-Policy',
              value: "camera=(); battery=(self); geolocation=(); microphone=('https://somewhere.com')",
            },
          ],
        },
      ];
  },
};

In the above example, we set our security headers for all routes in our site indicated by the source /(.*). You could also set security headers on a page-by-page basis like this ๐Ÿ‘‡

// next.config.js
module.exports = {
  async headers() {
    return [
        {
          source: '/profile',
          headers: [
            {
              key: 'Content-Security-Policy',
              value:
                "default-src 'self'; font-src 'self' 'https://fonts.googleapis.com'; img-src 'self' *.somewhere.com; script-src 'self'",
            }
          ]
        },
       {
          source: '/blog',
          headers: [
            {
              key: 'Content-Security-Policy',
              value:
                "default-src 'self'",
            }
          ]
        },
      ];
  },
};

Now spin up a development server in your Next.js application and navigate to the route after setting your security headers. You should be able to the headers you set in the network tab of the developer console.

Network tab in developer console showing the security headers we just set

Congratulations, your site is now more secure than it was before! Customize the security headers to suit your application as each site has its own specific requirements.

The end!

That was a look at HTTP security headers and how we can implement them in a Next.js application. I hope you managed to learn something new.

You can follow me @Kieran6dev and if you liked the article, please let me know about it.

Until next time everyone!

Did you find this article valuable?

Support Kieran Roberts by becoming a sponsor. Any amount is appreciated!

ย