16

I am currently making a Content Security Policy (CSP) for a production application made with Next.js. While I have found trustworthy documentation for implementing a CSP with the framework, there are a couple of concerns that I want to make sure are addressed correctly.

Issue #1: I have read that security policies set in HTTP headers are preferable. However, I cannot find a way to pass a 'nonce' attribute for inline styles in production using this approach. https://nextjs.org/docs/advanced-features/security-headers

Issue #2: I've seen other examples where developers inject their CSP in the custom document("./pages/_document.js"). I am hesitant to use this approach because I hear meta-tag CSPs are easily bypassable. https://github.com/vercel/next.js/tree/canary/examples/with-strict-csp

My Questions:

  1. Is there a way to use a 'nonce' with header configuration in "next.config.js"? If so, how?
  2. Is specifying "unsafe-inline" for styles in production a security issue at all if Next.js automatically sanitizes user input? I should also mention that I sanitize all mongo database queries in my APIs as well.
  3. Is there something about the meta tag approach described in Issue #2 that makes it as secure as the HTTP header approach?
  4. What approach do you recommend I take to make my CSP as strong as possible for my web app?

All the best, -Sam

Sam Morgan
  • 191
  • 1
  • 1
  • 4
  • We now have official documentation for handling nonces and Content Security Policy: https://nextjs.org/docs/app/building-your-application/configuring/content-security-policy – leerob Sep 01 '23 at 23:26

1 Answers1

3

NextJS has 2 pre-rendering mode: Static Site Generation(SSG) and Server-Side Rendering(SSR). The first one has no way to update nonce='value' in the HTML code, but when SSG you can pass a 'nonce' attribute for inline styles and scripts using ./pages/_document.tsx.

See an example for CSP header and meta tag CSP example.

Re Quersions:

  1. I think using a next.config.js is possible, for example next-safe package adds a nextSafe() function into this file to set a lot of headers:

.

const nextSafe = require('next-safe')
const isDev = process.env.NODE_ENV !== 'production'
module.exports = {
  async headers () {
    return [
    {
      source: '/:path*',
      headers: nextSafe({ isDev }),
    },
      ]
  },
}

To set 'nonce' into CSP header you can craft your own function in this way. To set 'nonce' attribute into styles you can use _document.tsx renderer.

  1. Specifying 'unsafe-inline' for styles in production is not a security issue. For example https://accounts.google.com page allows inline styles (it even does not have style-src/default-src directives but it carefully controls scripts).

  2. Setting CSP in HTTP headers is preferable but this does not mean that CSP in meta tag is easily bypassable. Just CSP in meta tag has some restrictions and if you do not use features that have been restricted you can safely use meta tag to delivery CSP.

  3. You can strengthen the protection indefinitely, spending a lot of time and resources. Just follow the main principle "protection should not be more expensive than the protected object".

granty
  • 7,234
  • 1
  • 14
  • 21
  • Thank you for your response. I should mention that I am using "next-secure-headers". I am unclear about your response to my first question. You said, "To set 'nonce' into CSP header you can craft your own function in this way." Are you saying that a function can be passed via "next.config.js" that will be accessible in "_document.tsx"? Also, I know that "unsafe-inline" isn't a security issue like scripts are. However, I want to make sure it is impossible for anyone to inject their own css into the page. Will specifying "unsafe-inline" make me vulnerable to this with Next.js? – Sam Morgan Nov 17 '21 at 17:25
  • (1) I wanted to say that it looks like `next.config.js` can call a function from the global namespace. In this case, this function will have access to the `'nonce'` value. You can check it in the `next-safe` package code. (2) Of course, `'unsafe-inline'` in the `style-src` technically allows to inject inline CSS. But without script injection, there is a very narrow circle of CSS-based scriptless XSS. Therefore, more attention is paid to scripts rather than styles. – granty Nov 18 '21 at 05:56
  • I will give that A try and let you know how it goes. Thank you again. – Sam Morgan Nov 18 '21 at 21:43
  • I can see that you can add an "nonce" value to the "nextSafe" function. However, none of the html elements seem to receive the nonce because there are violation errors in the console. Would you be willing to show me some example code showing how you'd put this together? Thank you very much. – Sam Morgan Nov 19 '21 at 18:11
  • Links to official examples of code are in the answer: [example for CSP header](https://github.com/vercel/next.js/blob/canary/examples/styled-jsx-with-csp/pages/_document.jsx) and [meta tag CSP example](https://stackoverflow.com/questions/65551212/using-csp-in-nextjs-nginx-and-material-uissr/65911555#65911555). Did you read these? – granty Nov 20 '21 at 08:46
  • I did see these, yes. I was trying to use an ‘nonce’ with the header instead of the meta tag approach but I guess that isn’t possible. There seems to be absolutely no way to use next.config.js with nonce values that will be passed to the DOM. These examples create a hash client side and insert a CSP meta tag on every application page. The only thing I wanted to avoid was ‘unsafe-inline’ for styles in production. If you say it isn’t a risk for Next JS applications, I believe you and will stick with the header approach. Thank you for all of your help. – Sam Morgan Nov 21 '21 at 18:55
  • [Here](https://bitgate.cz/content-security-policy-inline-scripts-and-next-js/) is an example how to pass `'nonce'` into `_document.tsx`, may be it help you. Yes. it's used to `` but you can adopt it for ` – granty Nov 22 '21 at 04:28