如何在Next.js中为内联样式和脚本添加随机数(nonce)?

huangapple go评论75阅读模式
英文:

How to add nonce to inline styles and scripts in Next.js?

问题

假设存在以下 Content-Security-Policy 策略:

default-src 'self'; script-src https://ray.run

由于以下错误,Next.js 无法正常运行:

> 拒绝应用内联样式,因为它违反了以下内容安全策略指令:"default-src 'self'"。要启用内联执行,需要使用"unsafe-inline"关键字、哈希('sha256-zu3j41gcw4FOWZYIiUz/VyL9HYakAfKLQFMCEcWNkAc=')或一个随机数('nonce-... ')注意,哈希不适用于事件处理程序、样式属性和JavaScript导航,除非存在"unsafe-hashes"关键字。还请注意,未明确设置"style-src",因此将使用"default-src"作为后备。
>
> 拒绝执行内联脚本,因为它违反了以下内容安全策略指令:"script-src https://ray.run"。要启用内联执行,需要使用"unsafe-inline"关键字、哈希('sha256-qJ8ozvdSL8lvQo3N0L0SYjL5lY0bLZcaD/xXL4BlEpQ=')或一个随机数('nonce-... ')。

根据错误提醒,我有两个选项:允许 "unsafe-inline" 或添加 nonce。然而,在阅读 Next.js 文档时,我无法找到如何将 nonce 添加到内联样式和脚本中的任何提及。

为了提供背景信息,我正在使用带有 React Server Component (RSC) 的 Next.js 版本13。

英文:

Assuming Content-Security-Policy policy such as:

default-src 'self'; script-src https://ray.run

Next.js fails to function because of errors such as:

> Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-zu3j41gcw4FOWZYIiUz/VyL9HYakAfKLQFMCEcWNkAc='), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present. Note also that 'style-src' was not explicitly set, so 'default-src' is used as a fallback.
>
> Refused to execute inline script because it violates the following Content Security Policy directive: "script-src https://ray.run". Either the 'unsafe-inline' keyword, a hash ('sha256-qJ8ozvdSL8lvQo3N0L0SYjL5lY0bLZcaD/xXL4BlEpQ='), or a nonce ('nonce-...') is required to enable inline execution.

As per what the error suggests, I have two options: allow "unsafe-inline" or add nonce. However, reading Next.js documentation I cannot find any mention of how to nonce to the inlined styles and scripts.

For context, I am using [tag:next.js13] with React Server Component (RSC).

答案1

得分: 4

要向<script><styles>标签添加nonce属性,您需要在middleware.ts文件中定义content-security-policy头部(适用于next@13.4.0及更高版本)。

Next.js会解析该头部(在renderToHTMLOrFlight函数中)以提取nonce值,然后将其添加到生成的HTML标签中。

以下是middleware函数的示例:

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const nonce = crypto.randomUUID();
  const csp = `script-src 'nonce-${nonce}';`;

  // 克隆请求头部
  const requestHeaders = new Headers(request.headers);

  // 设置CSP头部,以便Next.js可以读取它并生成带有nonce的标签
  requestHeaders.set('content-security-policy', csp);

  // 创建新的响应
  const response = NextResponse.next({
    request: {
      // 新的请求头部
      headers: requestHeaders
    }
  });

  // 也在响应中设置CSP头部,以便输出到浏览器
  response.headers.set('content-security-policy', csp);

  return response;
}

如果需要,您还可以添加一个自定义请求头来读取页面中的nonce值:

requestHeaders.set('x-nonce', nonce);

根据mdn文档的说明:

Nonces应该在每次页面加载时以不同的方式生成(只生成一次nonce!)。

这意味着您不能在next.config.js文件中设置它,因为它只会在构建时生成一次。在middleware函数中,nonce值将在每个请求中重新生成。

最后但并非最不重要的,这仅适用于动态路由,而不适用于静态渲染,因为nonce值必须在每次页面加载时不同。


代码最初由@danieltott在GitHub上发布:https://github.com/vercel/next.js/issues/43743#issuecomment-1542684019

相关讨论:

英文:

To add the nonce attribute to &lt;script&gt; and &lt;styles&gt; tags, you need to define the content-security-policy header in the middleware.ts file (works with next@13.4.0 and higher).

The header is parsed by Next.js (in the renderToHTMLOrFlight function) to extract the nonce value, which is then added to the generated HTML tags.

Here's an example of the middleware function:

import { NextResponse } from &#39;next/server&#39;;
import type { NextRequest } from &#39;next/server&#39;;

export function middleware(request: NextRequest) {
  const nonce = crypto.randomUUID();
  const csp = `script-src &#39;nonce-${nonce}&#39;;`;

  // Clone the request headers
  const requestHeaders = new Headers(request.headers);

  // Set the CSP header so that Next.js can read it and generate tags with the nonce
  requestHeaders.set(&#39;content-security-policy&#39;, csp);

  // Create new response
  const response = NextResponse.next({
    request: {
      // New request headers
      headers: requestHeaders
    }
  });

  // Also set the CSP header in the response so that it is outputted to the browser
  response.headers.set(&#39;content-security-policy&#39;, csp);

  return response;
}

If necessary, you can also add a custom request header to read the nonce value in your pages:

requestHeaders.set(&#39;x-nonce&#39;, nonce);

To go further, according to the mdn documentation:
> Nonces should be generated differently each time the page loads (nonce only once!).

This means you can't set it in the next.config.js file, as it will only be generated once at build time. In the middleware function, the nonce value will be regenerated for each request.

Last but not least, this only works with dynamic routes and not with static rendering, since the nonce value must be different for each page load.


Code originaly published by @danieltott on GitHub: https://github.com/vercel/next.js/issues/43743#issuecomment-1542684019

Related discussions:

答案2

得分: 0

只是额外信息,如果您添加了一次性随机数并生成了一份灯塔报告,它仍然会抱怨:“考虑添加'unsafe-inline'(被支持nonce/hashes的浏览器忽略)以向较旧的浏览器兼容”,这个问题的严重性被标记为中等。在我的情况下,我同时添加了随机数和unsafe-inline。

英文:

Just an additional info, If you add nonce and generate a lighthouse report it will still complain: "Consider adding 'unsafe-inline' (ignored by browsers supporting nonces/hashes) to be backward compatible with older browsers.", and the severity for this one is labeled as Medium. In my case, I added both the nonce and the unsafe-inline.

huangapple
  • 本文由 发表于 2023年5月29日 02:47:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76353091.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定