Spring Boot 和 PrimeReact 的 CSP 问题

问题描述 投票:0回答:1

我们有一个单页面应用程序,它使用 Spring Boot 作为后端,React 作为前端。我们使用 PrimeReact 作为 React 的组件库。 我们将 React 应用程序打包为 Spring Boot 应用程序中的静态部分,这样我们只有一个部署文件,并且 React 应用程序由 Spring Boot 提供给用户..

更新我们的 Node.js 包后,PrimeReact 在结果页面的 body 元素中注入 nonced 内联样式。在 Spring Boot 配置中静态创建并添加到 HTTP 标头 bean 的 CSP 不喜欢随机数,并且该站点看起来确实没有样式..

Chrome 多次显示此错误:

"Refused to execute inline script because it violates the folowwing Content Security Policy directive: "script src 'self'". Either the 'unsafe-inline' keyword, a has ('sha256-*EXPLICIT-HASH-EVERY-TIME*'), or a nonce ('nonce-...') is required to enable inline execution"

我知道,为什么浏览器拒绝使用这些哈希值。但我不知道如何解决这个问题。

原包装:

  "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "@types/jest": "^27.5.2",
    "@types/node": "^16.18.46",
    "@types/react": "^18.2.21",
    "@types/react-dom": "^18.2.7",
    "chart.js": "^4.4.0",
    "chartjs-plugin-datalabels": "^2.2.0",
    "http-proxy-middleware": "^2.0.6",
    "primeicons": "^6.0.1",
    "primereact": "^9.6.2",
    "quill": "^1.3.7",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.15.0",
    "react-scripts": "5.0.1",
    "react-transition-group": "^4.4.5",
    "typescript": "^4.9.5",
    "web-vitals": "^2.1.4"
  }, 

更新包:

  "dependencies": {
    "@testing-library/jest-dom": "^6.5.0",
    "@testing-library/react": "^16.0.1",
    "@testing-library/user-event": "^14.5.2",
    "@types/jest": "^29.5.12",
    "@types/node": "^22.5.4",
    "@types/react": "^18.3.5",
    "@types/react-dom": "^18.3.0",
    "chart.js": "^4.4.4",
    "chartjs-plugin-datalabels": "^2.2.0",
    "http-proxy-middleware": "^3.0.2",
    "primeicons": "^7.0.0",
    "primereact": "^10.8.2",
    "quill": "^2.0.2",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-router-dom": "^6.26.1",
    "react-scripts": "^5.0.1",
    "react-transition-group": "^4.4.5",
    "typescript": "^4.9.5",
    "web-vitals": "^4.2.3"
  },

PrimeReact 在 HTML-body 元素注入样式时在这个地方(hooks.esm.js)抛出异常:

  var load = function load() {
    if (!document || isLoaded) {
      return;
    }
    var styleContainer = (context === null || context === void 0 ? void 0 : context.styleContainer) || document.head;
    styleRef.current = getCurrentStyleRef(styleContainer);
    if (!styleRef.current.isConnected) {
      styleRef.current.type = 'text/css';
      if (id) {
        styleRef.current.id = id;
      }
      if (media) {
        styleRef.current.media = media;
      }
      DomHandler.addNonce(styleRef.current, context && context.nonce || PrimeReact.nonce);
      styleContainer.appendChild(styleRef.current);
      if (name) {
        styleRef.current.setAttribute('data-primereact-style-id', name);
      }
    }
    styleRef.current.textContent = css;
    setIsLoaded(true);
  };

上面的代码是在新版本的 PrimeReact 中引入的,在 PrimeReact 9.6.2 中没有这段代码。我还没有找到为什么现在会发生这种情况以及原因的线索。

Spring Boot 中的 CSP 生成:

contentSecurityPolicyConfig -> contentSecurityPolicyConfig.policyDirectives("default-src 'self'; img-src 'self' data:;")

我们当前使用的不安全解决方法:

contentSecurityPolicyConfig -> contentSecurityPolicyConfig.policyDirectives("default-src 'self' 'unsafe-inline'; img-src 'self' data:;")

我对此有一些疑问:

  • 为什么 PrimeReact 现在使用内联样式?难道它们不是坏事吗?
  • 是否可以阻止 PrimeReact 使用此类内联样式(并且不回到旧版本)?对我来说,这可能与一些我不知道的配置错误有关。
  • 我已经看到,Spring Boot 中有一个选项可以生成随机数并在动态创建的 CSP 中使用它们。此外,PrimeReact 集中保存其随机数。但如果创建随机数的 javascript 文件由 Spring Boot tomcat 静态提供服务,我无法想象如何使这两个文件可互操作。没有办法让这项工作以这种方式进行,对吗?
  • 是否有必要使用 HTML 中包含的 CSP?是否有必要将 HTTP 标头 CSP 全部替换为 HTML CSP 才能使其正常工作?

任何暗示我正确(并且可能是最好)方向的答案都将受到赞赏。预先感谢。

reactjs spring content-security-policy boot primereact
1个回答
0
投票

我们找到了解决方案:

我们现在使用 HTML 内 CSP。在 React 中,我们为当前 HTML 页面生成一个随机数并将其传递给 PrimeReact。 乍一看,这看起来有点不安全:将内容传输到客户端并让客户端构建 csp,但实际上这只是对页面发出的一个请求,并且它会密封内容,以便其他内容无法被访问。已加载 csp 未覆盖的内容。

const array = new Uint32Array(1);
const nonce = crypto.getRandomValues(array)[0].toString();

function initCSP() {

let csp = "default-src 'self'; img-src 'self' data:; style-src 'self' 'nonce-{$nonce$}'".replace("{$nonce$}", nonce);
var meta = document.createElement('meta');
meta.httpEquiv = "Content-Security-Policy";
meta.content = csp;

let updated = false;

for (const element of document.getElementsByTagName('head')[0].children) {
    if (element.getAttribute("http-equiv") === "Content-Security-Policy") {
        element.setAttribute("content", csp);
        updated = true;
    }
}

if (!updated) {
    document.getElementsByTagName('head')[0].insertBefore(meta, document.getElementsByTagName('head')[0].firstChild)
}

return nonce;
}

--

const config = {
    nonce: nonce
}

--

<PrimeReactProvider value={config}>
...
</PrimeReactProvider>

有些代码还不是最优的,但我想你可能明白了。

© www.soinside.com 2019 - 2024. All rights reserved.