csp 能有效防御多数非持久型、反射型 xss,但无法拦截 eval()、unsafe-inline/unsafe-eval 等动态执行方式;正确配置需用 flask-talisman 或严格手写 header,配合 nonce、report-only 过渡及全链路资源梳理。

什么是 CSP,它真能防住 XSS 吗
CSP(Content Security Policy)不是万能补丁,但它确实能拦下绝大多数非持久型、反射型 XSS,尤其是那些靠 <script></script> 标签或内联事件(如 onclick)触发的攻击。它的核心逻辑是:浏览器只执行白名单里允许的脚本,其他一律拒绝——哪怕 HTML 已被注入恶意代码。
但要注意:eval()、setTimeout("string")、内联 javascript: URL 这类动态执行方式,只要没显式禁用,CSP 就管不住;更关键的是,如果策略里写了 unsafe-inline 或 unsafe-eval,等于主动开门。
如何在 Flask 中正确设置 CSP 响应头
Flask 默认不发 CSP,必须手动加 response.headers['Content-Security-Policy']。别用 app.after_request 简单拼字符串,容易漏掉关键指令或被覆盖。
- 优先用
flask-talisman扩展,它自动处理 header 优先级、nonce 生成和 script 标签注入,比手写可靠得多 - 如果手写,至少包含
default-src 'self'和script-src 'self',再根据需要加https://cdn.example.com这类外域 - 绝对避免写成
script-src 'unsafe-inline'—— 即便你用了nonce,这个指令也会让所有内联脚本绕过检查 - 开发时打开浏览器 DevTools 的 Console,留意
Refused to execute inline script这类提示,那是 CSP 在起作用,不是报错
为什么加了 nonce 还被拦截?常见配置错误
nonce 不是“加了就完事”,它必须同时满足三个条件才生效:响应头里的 script-src 包含 nonce-xxxx、HTML 中的 <script nonce="xxxx"></script> 标签匹配、且该标签不能是通过 innerHTML 插入的(DOM 动态插入的 script 永远不认 nonce)。
- Flask 模板里用
{{ csp_nonce() }}(来自 talisman)比硬编码字符串安全;自己生成的话,每次请求必须新随机,不能复用 - Webpack/Vite 构建的 bundle 脚本属于外部文件,不需要 nonce,但得确保它们的 URL 在
script-src白名单里 - Vue/React 的内联模板表达式(如
@click="doX()")不算 script,CSP 不管;但v-html渲染的内容如果含 script 标签,照样被拦
生产环境 CSP 必须关掉 report-only 吗
不能直接关。上线前务必先用 Content-Security-Policy-Report-Only 头跑至少 3–5 天,收集真实用户触发的 violation reports(发到你自己的 endpoint)。很多问题只在特定浏览器、插件或用户行为下暴露。
- report-uri 或 report-to 字段必须指向你可控的服务端接口,别用第三方上报服务——数据格式、频率、隐私都不可控
- 常见误报来源:浏览器扩展注入的脚本、CDN 加载失败回退到本地、甚至某些旧版微信内置浏览器会伪造 nonce
- 切到 enforce 模式后,一旦策略写死,任何漏掉的资源都会直接 404 或空白,而不是静默降级
真正难的不是写对那几行 header,而是搞清你的前端到底加载了多少隐式依赖:字体、埋点 SDK、A/B 测试脚本、广告 iframe……这些全得列进 CSP 才算落地。漏一个,就可能被绕过。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











