如何在NextJS、nginx和Materialui(SSR)中实施CSP以增强网站安全性?
- 内容介绍
- 文章标签
- 相关推荐
本文共计731个文字,预计阅读时间需要3分钟。
TLDR:我在使用Material-UI(服务器端渲染)为NextJS设置CSP时遇到问题。
TLDR:我在使用Material-UI(服务器端渲染)为NextJS设置CSP并由Nginx(使用反向代理)提供服务时遇到问题。目前我在加载Material-TLDR:我在使用 Material-UI(服务器端渲染)为 NextJS 设置 CSP 并由 Nginx(使用反向代理)提供服务时遇到问题。
目前我在加载 Material-UI 样式表和加载我自己的样式时遇到问题
使用makeStyles来自@material-ui/core/styles
笔记:
- 按照material-ui.com/styles/advanced/#next-js开启 SSR
- github.com/mui-org/material-ui/tree/master/examples/nextjs
- 我查看了material-ui.com/styles/advanced/#how-does-one-implement-csp但我不确定如何让 nginx 遵循这些nonce值,因为 nonce 生成为不可预测的字符串.
default.conf (nginx)
# www.acunetix.com/blog/web-security-zone/hardening-nginx/upstream nextjs_upstream { server localhost:3000; # We could add additional servers here for load-balancing}server { listen $PORT default_server; # redirect $host$request_uri redirect; # } server_name _; server_tokens off; proxy_fonts.googleapis.com/css?family=Roboto:300,400,500,700 form-action 'none'; frame-ancestors 'none'; base-uri 'none';" always; add_header X-XSS-Protection "1; mode=block"; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; location / { # limit request types to HTTP GET # ignore everything else limit_except GET { deny all; } proxy_pass nextjs_upstream; }}
回答
我找到的解决方案是在 _document.tsx 中向内联 js 和 css 添加 nonce 值
_document.tsx
使用 uuid v4 生成随机数,并使用 crypto nodejs 模块将其转换为 base64。然后创建内容安全策略并添加生成的 nonce 值。创建一个函数来完成创建随机数并生成 CSP 并与随机数一起返回 CSP 字符串
在 HTML Head 中添加生成的 CSP 并添加元标记。
import React from 'react';import Document, { Html, Head, Main, NextScript } from 'next/document';import { ServerStyleSheets } from '@material-ui/core/styles';import crypto from 'crypto';import { v4 } from 'uuid';// import theme from '@utils/theme';/** * Generate Content Security Policy for the app. * Uses randomly generated nonce (base64) * * @returns [csp: string, nonce: string] - CSP string in first array element, nonce in the second array element. */const generateCsp = (): [csp: string, nonce: string] => { const production = process.env.NODE_ENV === 'production'; // generate random nonce converted to base64. Must be different on every HTTP page load const hash = crypto.createHash('sha256'); hash.update(v4()); const nOnce= hash.digest('base64'); let csp = ``; csp += `default-src 'none';`; csp += `base-uri 'self';`; csp += `style-src fonts.googleapis.com 'unsafe-inline';`; // NextJS requires 'unsafe-inline' csp += `script-src 'nonce-${nonce}' 'self' ${production ? '' : "'unsafe-eval'"};`; // NextJS requires 'self' and 'unsafe-eval' in dev (faster source maps) csp += `font-src fonts.gstatic.com;`; if (!production) csp += `connect-src 'self';`; return [csp, nonce];};export default class MyDocument extends Document { render(): JSX.Element { const [csp, nonce] = generateCsp(); return ( {/* PWA primary color */} {/* */} rel='stylesheet' href='fonts.googleapis.com/css?family=Roboto:300,400,500,700 }}// `getInitialProps` belongs to `_document` (instead of `_app`),// it's compatible with server-side generation (SSG).MyDocument.getInitialProps = async (ctx) => { const sheets = new ServerStyleSheets(); const originalRenderPage = ctx.renderPage; ctx.renderPage = () => originalRenderPage({ enhanceApp: (App) => (props) => sheets.collect(), }); const initialProps = await Document.getInitialProps(ctx); return { ...initialProps, // Styles fragment is rendered after the app and page rendering finish. styles: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()], };};
来源:bitgate.cz/content-security-policy-inline-scripts-and-next-js/
本文共计731个文字,预计阅读时间需要3分钟。
TLDR:我在使用Material-UI(服务器端渲染)为NextJS设置CSP时遇到问题。
TLDR:我在使用Material-UI(服务器端渲染)为NextJS设置CSP并由Nginx(使用反向代理)提供服务时遇到问题。目前我在加载Material-TLDR:我在使用 Material-UI(服务器端渲染)为 NextJS 设置 CSP 并由 Nginx(使用反向代理)提供服务时遇到问题。
目前我在加载 Material-UI 样式表和加载我自己的样式时遇到问题
使用makeStyles来自@material-ui/core/styles
笔记:
- 按照material-ui.com/styles/advanced/#next-js开启 SSR
- github.com/mui-org/material-ui/tree/master/examples/nextjs
- 我查看了material-ui.com/styles/advanced/#how-does-one-implement-csp但我不确定如何让 nginx 遵循这些nonce值,因为 nonce 生成为不可预测的字符串.
default.conf (nginx)
# www.acunetix.com/blog/web-security-zone/hardening-nginx/upstream nextjs_upstream { server localhost:3000; # We could add additional servers here for load-balancing}server { listen $PORT default_server; # redirect $host$request_uri redirect; # } server_name _; server_tokens off; proxy_fonts.googleapis.com/css?family=Roboto:300,400,500,700 form-action 'none'; frame-ancestors 'none'; base-uri 'none';" always; add_header X-XSS-Protection "1; mode=block"; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; location / { # limit request types to HTTP GET # ignore everything else limit_except GET { deny all; } proxy_pass nextjs_upstream; }}
回答
我找到的解决方案是在 _document.tsx 中向内联 js 和 css 添加 nonce 值
_document.tsx
使用 uuid v4 生成随机数,并使用 crypto nodejs 模块将其转换为 base64。然后创建内容安全策略并添加生成的 nonce 值。创建一个函数来完成创建随机数并生成 CSP 并与随机数一起返回 CSP 字符串
在 HTML Head 中添加生成的 CSP 并添加元标记。
import React from 'react';import Document, { Html, Head, Main, NextScript } from 'next/document';import { ServerStyleSheets } from '@material-ui/core/styles';import crypto from 'crypto';import { v4 } from 'uuid';// import theme from '@utils/theme';/** * Generate Content Security Policy for the app. * Uses randomly generated nonce (base64) * * @returns [csp: string, nonce: string] - CSP string in first array element, nonce in the second array element. */const generateCsp = (): [csp: string, nonce: string] => { const production = process.env.NODE_ENV === 'production'; // generate random nonce converted to base64. Must be different on every HTTP page load const hash = crypto.createHash('sha256'); hash.update(v4()); const nOnce= hash.digest('base64'); let csp = ``; csp += `default-src 'none';`; csp += `base-uri 'self';`; csp += `style-src fonts.googleapis.com 'unsafe-inline';`; // NextJS requires 'unsafe-inline' csp += `script-src 'nonce-${nonce}' 'self' ${production ? '' : "'unsafe-eval'"};`; // NextJS requires 'self' and 'unsafe-eval' in dev (faster source maps) csp += `font-src fonts.gstatic.com;`; if (!production) csp += `connect-src 'self';`; return [csp, nonce];};export default class MyDocument extends Document { render(): JSX.Element { const [csp, nonce] = generateCsp(); return ( {/* PWA primary color */} {/* */} rel='stylesheet' href='fonts.googleapis.com/css?family=Roboto:300,400,500,700 }}// `getInitialProps` belongs to `_document` (instead of `_app`),// it's compatible with server-side generation (SSG).MyDocument.getInitialProps = async (ctx) => { const sheets = new ServerStyleSheets(); const originalRenderPage = ctx.renderPage; ctx.renderPage = () => originalRenderPage({ enhanceApp: (App) => (props) => sheets.collect(), }); const initialProps = await Document.getInitialProps(ctx); return { ...initialProps, // Styles fragment is rendered after the app and page rendering finish. styles: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()], };};
来源:bitgate.cz/content-security-policy-inline-scripts-and-next-js/

