接React SSR实现,本文在此基础上去实现CSP安全策略,用于预防XSS攻击
四、material-ui实现CSP
4.1前提条件:
首先按照material-ui5官方案例让你的React项目已经完成了ssr(服务端)渲染
可以参见文章:https://blog.csdn.net/daisyboom/article/details/133866051?spm=1001.2014.3001.5501
- 官方文档参考:Content Security Policy (CSP) - Material UI (mui.com)
- 示例:https://gitee.com/dengjingjing0408/ssr-server-rendering-demo
- 我根据文档以及github实现了一个demo,属实搜了好多资料和讨论呀!!
4.2 开始实现
本人理解csp主要思路就是通过在CSP 标头中设置一个随机数nonce,限制所有的style文件、script脚本都必须携带正确的nonce值,才能允许访问对应的资源,同时该nonce在每次请求后都会发生变化,以此来防止XSS攻击!
下面按照上文提供的demo基础上,开始实现csp,要考虑的是:
- 在server端中间件中生成的nonce该怎么传给client端?
- 在client端中
import './xxxx.css文件会形成一个
①第一步:按照官方示例:内容安全策略 (CSP) - 材料用户界面 (mui.com)
-
在server端的中间件中生成nonce,并设置到res.locals.styleNonce中
server.tsx
import express from "express"; import {v4} from "uuid"; const app = express(); app.use((req, res, next) => { // nonce should be base64 encoded res.locals.Nonce = Buffer.from(v4()).toString("base64"); next(); }); -
设置csp标头
server.tsx
server.get("/", (req, res) => { //set http header csp res.setHeader("Content-Security-Policy-Report-Only", "default-src 'self'; style-src 'self' 'nonce-" + res.locals.Nonce + "'; script-src 'self' 'nonce-" + res.locals.Nonce + "'; report-uri http://localhost:5599/listen"); }); -
在每次请求时,将res.locals.styleNonce存储的新随机数nonce,发给设置的模板引擎中将nonce存储在window.NONCE_ID中
- 我使用的是ejs模板引擎
server.tsx
server.get("/", (req, res) => { //set http header csp res.setHeader("Content-Security-Policy-Report-Only", "default-src 'self'; style-src 'self' 'nonce-" + res.locals.Nonce + "'; script-src 'self' 'nonce-" + res.locals.Nonce + "'; report-uri http://localhost:5599/listen"); res.render("index", { html, css ,Nonce: res.locals.Nonce}); });index.ejs
- ejs中server端res.render发来的参数,用<%%>进行解析
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>React-ssr-csp-material-ui</title> <%- css %> </head> <body> <div id="root"><%- html %></div> <script nonce="<%=styleNonce%>">window.NONCE_ID = "<%=styleNonce%>";</script> <script defer="defer" src="xxxxxxx.client.js"></script> </body> </html> -
在client端通过window[“NONCE_ID”],就可以获取到服务端设置的nonce值
client.tsx
const __webpack_nonce__ = window["NONCE_ID"];
②第二步:解决自行导入css文件,导致的报错
【存在问题】
-
以下是因为在client端,自行引入的css文件,并没有设置随机数nonce,因而被拒绝


【解决方法】
-
在server端创建create-nonce.js,放入以下代码
- 注意
/* global __webpack_nonce__ */不可以删除。否则文件编译报错 - 它表示在该变量之后,导入的所有style/script标签,都会为其加上一个在window.NONCE_ID上设置的nonce值
/* global __webpack_nonce__ */ // eslint-disable-line no-unused-vars __webpack_nonce__ = window.NONCE_ID; - 注意
-
在client端的client.tsx中导入create-nonce文件
注意:必须在.css文件之前导入!!import "../server/create-nonce"; import "./index.css";//在client中导入的css文件 -
再次运行可以看到
4.3 Demo地址
4.4 常见报错
①遇到内联地址报错的问题:
方案一:可通过csp允许不安全的内联样式
res.setHeader("Content-Security-Policy-Report-Only", "default-src 'self'; style-src 'unsafe-inline' " + "'; script-src 'self' 'nonce-" + res.locals.Nonce + "'; report-uri http://localhost:5599/listen");方案二:依次添加白名单,在unsafe-hashes后添加控制台报错的hash
res.setHeader("Content-Security-Policy-Report-Only", "default-src 'self'; style-src 'self' 'nonce-" + res.locals.styleNonce + "' 'unsafe-hashes' 'sha256-fQ9khXcyZvQRWl/5riXDoZbI9F1nSP745q4aJkJvFXs='" + "'; script-src 'self' 'nonce-" + res.locals.Nonce + "'; report-uri http://localhost:5599/listen");4.5 参考文档
-
material-ui CSP实现
-
官方文档:
-
请求头
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP
-
-
利用__ webpack_nonce __实现nonce共享
-
在server端生成nonce
-
传nonce给client端
-
为内联
-
-


被折叠的 条评论
为什么被折叠?



