# Securing your Next.js 13 application

> Next.js is a popular framework for building server-side rendered React applications, but like any web application, it's crucial to take security seriously.

*Published: 2023-04-22 · Tag: security*

---

Next.js is a popular framework for building server-side rendered React
applications, but like any web application, it's crucial to take security
seriously. In this blog post, we will discuss some essential security practices
that you can implement in your Next.js application. We will cover topics such
as security headers, including Content Security Policy, Referrer-Policy, and
X-Frame-Options, as well as preventing cross-site request forgery attacks. By
following these security best practices, you can help ensure the safety and
privacy of your users' sensitive information.

## Security Headers

One of the essential security practices for Next.js applications is to
implement recommended security headers. Security headers are HTTP response
headers that allow web developers to control and enforce additional security
mechanisms in their applications. The code snippet below shows an example of
some recommended security headers to add to your Next.js application.

```js title="Adding security headers to your next.config.mjs file"
const ContentSecurityPolicy = `
  default-src 'self' vercel.live;
  script-src 'self' 'unsafe-eval' 'unsafe-inline' cdn.vercel-insights.com vercel.live;
  style-src 'self' 'unsafe-inline';
  img-src * blob: data:;
  media-src 'none';
  connect-src *;
  font-src 'self';
`.replace(/\n/g, '');

const securityHeaders = [
  { key: 'Content-Security-Policy', value: ContentSecurityPolicy },
  { key: 'Referrer-Policy', value: 'origin-when-cross-origin' },
  { key: 'X-Frame-Options', value: 'DENY' },
  { key: 'X-Content-Type-Options', value: 'nosniff' },
  { key: 'X-DNS-Prefetch-Control', value: 'on' },
  { key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains; preload' },
  { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
];

/** @type {import('next').NextConfig} */
export default {
  headers() {
    return [
      { source: '/(.*)', headers: securityHeaders },
    ];
  }
}
```

### Content Security Policy

Content Security Policy (CSP) is a security header that allows you to specify a
set of rules that define the sources from which the application can load
resources such as scripts, styles, and images. It is designed to mitigate
cross-site scripting (XSS) attacks by preventing the execution of scripts from
untrusted sources. The value of the Content-Security-Policy header in the code
snippet above specifies the allowed sources for various resources.

### Referrer-Policy

Referrer-Policy is a security header that allows you to control how much
information is passed in the HTTP Referer header when navigating from one page
to another. The value of the Referrer-Policy header in the code snippet above
specifies that the origin should be sent as the referrer when the request is
made from the same origin, but not when the request is made from a different
origin.

### X-Frame-Options

X-Frame-Options is a security header that prevents clickjacking attacks by
ensuring that a webpage can only be displayed in a frame or iframe if it is
from the same origin. The value of the X-Frame-Options header in the code
snippet above specifies that the page should not be displayed in any frame or
iframe.

### X-Content-Type-Options

X-Content-Type-Options is a security header that prevents MIME type sniffing, a
vulnerability that allows attackers to trick the browser into interpreting a
resource as a different MIME type, possibly executing malicious code. The value
of the X-Content-Type-Options header in the code snippet above specifies that
the browser should not try to guess the MIME type and should only use the
declared MIME type.

### X-DNS-Prefetch-Control

X-DNS-Prefetch-Control is a security header that controls whether the browser
should prefetch DNS entries for links on a webpage. Prefetching DNS entries can
reduce the latency of subsequent requests but can also be used for tracking
purposes. The value of the X-DNS-Prefetch-Control header in the code snippet
above specifies that DNS prefetching should be enabled.

### Strict-Transport-Security

Strict-Transport-Security (HSTS) is a security header that enforces the use of
HTTPS by the browser and prevents downgrade attacks. The value of the
Strict-Transport-Security header in the code snippet above specifies that the
browser should use HTTPS for all requests for the next 365 days, including
subdomains, and preload the HSTS policy to all browsers.

### Permissions-Policy

Permissions-Policy is a security header that allows you to specify which APIs
and features are allowed to be used in the application. The value of the
Permissions-Policy header in the code snippet above specifies that the camera,
microphone, and geolocation APIs are not allowed to be used.

## Cross Site Request Forgery

**Cross-site request forgery (CSRF)** is a type of attack that exploits the
trust between a user and a website to perform unauthorized actions on the user's
behalf. CSRF attacks can lead to serious security breaches, such as account
takeover, data theft, and malware installation. In this type of attack, an
attacker tricks a user into executing a malicious action on a website without
their knowledge or consent. Since the attack is carried out with the user's
credentials, it can be difficult to detect and mitigate.

### Implement on your own

Here is a high level implementation

1. Create a `middleware.ts` on your root folder.
2. Filter middleware to grab only `GET` requests
3. Create a secure cookie with a short expiration date with a value that
  is signed by the secret key only known by the server-side available
  environment key.
4. Update the NextResponse headers with `X-CSRF-Token` header with the raw value
5. On the server-side of the page you want to add CSRF protection, grab the
  http value using `headers().get('X-CSRF-Token')` and pass it to the
  client-side component.
6. On the client-side of the page, create a hidden input with the value of the
  `X-CSRF-Token` header.
6. On your **route endpoint**:
   1. Validate that the CSRF header exists
   2. Validate that a cookie is sent with the existing request
   3. Sign `X-CSRF-Token` value with your secret key and make sure that it
     is same as the cookie of the request.
   4. If any of these validations fail, throw an error.

### Use a package

1. Install [`edge-csrf`](https://github.com/amorey/edge-csrf) module using
`npm install edge-csrf` or `yarn add edge-csrf`.
2. Create a `middleware.ts` on your root folder.

```js title="middleware.ts"
import csrf from 'edge-csrf';
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

// initalize protection function
const csrfProtect = csrf({
  cookie: {
    secure: process.env.NODE_ENV === 'production',
  },
});

export async function middleware(request: NextRequest) {
  const response = NextResponse.next();

  // csrf protection
  const csrfError = await csrfProtect(request, response);

  // check result
  if (csrfError) {
      return new NextResponse('invalid csrf token', { status: 403 });
  }

  return response;
}
```

3. And on your React component:

```js title="pages/page.ts"
import type { NextPage, GetServerSideProps } from 'next';
import React from 'react';

type Props = {
  csrfToken: string;
};

export const getServerSideProps: GetServerSideProps = async ({ res }) => {
  const csrfToken = res.getHeader('x-csrf-token') || 'missing';
  return { props: { csrfToken } };
}

const FormPage: NextPage<Props> = ({ csrfToken }) => {
  return (
    <form action="/api/form-handler" method="post">
      <input type="hidden" value={csrfToken}>
      <input type="text" name="my-input">
      <input type="submit">
    </form>
  );
}

export default FormPage;
```

4. Create your API endpoint:

```js title="pages/api/form-handler.ts"
import type { NextApiRequest, NextApiResponse } from 'next';

type Data = {
  status: string
};

export default function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
  // this code won't execute unless CSRF token passes validation
  res.status(200).json({ status: 'success' });
}
```

---

Authored by Yagiz Nizipli