门禁管的是"进去之后能干什么",CSP 管的是"恶意代码根本进不来"。本节讲内容安全策略的字段语义、Tauri 的自动 nonce 注入,然后把 XSS 到 RCE 的完整攻击路径走一遍,标出每一段的断点在哪。读完本节,你能对"注入"这个词给出工程级的防守方案,而不是一句"要防 XSS"。
内容安全策略是一串声明,告诉 WebView:这个页面允许从哪里加载什么类型的资源。浏览器内核负责执行——违反声明的外链脚本、内联事件、跨源请求会被直接拦下。Tauri 里 CSP 写在配置的 security 段:
{ "app": { "security": { "csp": "default-src 'self'; img-src 'self' asset: http://asset.localhost; style-src 'self' 'unsafe-inline'; connect-src 'self' ipc: http://ipc.localhost" } } }
按指令拆读:default-src 'self' 是底线——脚本、样式、字体等未单独声明的资源只许同源(打包进应用的那份);img-src 额外放行资源协议,供展示本地图片(asset 协议的使用见第 7 章文件场景);connect-src 放行 IPC 通道——这是 Tauri 特有的一条,命令调用走的 ipc 源必须写进去,漏写则前端所有 invoke 静默失败;style-src 的 unsafe-inline 是常见妥协——多数 UI 框架会注入内联样式,放开样式注入的风险远低于放开脚本。没有 script-src 的额外放行,脚本的来源被钉死在自身。
严格的 CSP 禁止内联脚本(script-src 不给 unsafe-inline),但 Tauri 自己要往页面注入初始化脚本——IPC 的桥就是注入进去的。两难:放开内联,注入通道大开;收死内联,框架自己跑不了。Tauri 的解法是 nonce 机制:构建时给注入脚本生成一次性随机标记,并把它追加进 CSP 的 script-src——于是"只有带这个随机标记的内联脚本"被放行,攻击者注入的脚本没有标记,照样被拦。这套机制默认开启且自动维护(你写的 CSP 字符串会被追加标记);与之配套的 dangerousDisableAssetCspModification 选项能关掉这个行为——别关,名字里的 dangerous 就是给你看的。

把最坏假设展开成四段路径,逐段看锁在哪:
第一段,恶意内容进入。 粘贴板、导入文件、第三方依赖都可能带恶意标记。此段的防线是入口净化——凡是要以 HTML 形式渲染的外来内容,先过净化库把脚本类节点剥干净;纯文本渲染(textContent 而非 innerHTML)是零成本的默认选择。
第二段,脚本执行(XSS 成立)。 就算净化漏了、脚本进了页面——CSP 的 nonce 与 self 钉死让它大概率跑不起来。这是断点一。注意"大概率":CSP 是纵深防御的一层,不是绝对屏障,别把它当唯一防线用。
第三段,冒充前端过桥。 脚本跑起来了,开始调命令——权限闸门按窗口授权集合放行,6.2 里"能少授就少授"的每一项节约,在这里变成攻击者的每一次碰壁。这是断点二。
第四段,系统能力失守(RCE)。 假设授权集合里确有 fs 读权限,脚本传进来的是精心构造的越界路径——5.3 的 ensure_within 与第 6.2 的作用域在此合围。参数校验是断点三,也是芯侧的最后一道。
四段推演的结论是纵深防御:三个断点互相独立,任何一层失守不引发连锁。安全审查时逐层自问"CSP 被绕过会怎样、权限被滥用会怎样、校验被跳过会怎样",三问都有答案,才叫有防线。
策略类配置最容易死在"不知道哪条指令拦的"。走一个真实场景:给笔记加"网页剪藏预览"功能,预览窗要显示外部站点的缩略图,上线测试时图片全裂,WebView 控制台里躺着一条拦截报告,大意是图片请求被内容安全策略拒绝、来源不符合 img-src 声明。这条报错的读法分三步:看动作(加载被拒)、看资源类型(图片,对应 img-src)、看来源(外部站点域名)——三个信息合起来,修正方案就唯一了:在预览窗的策略里给 img-src 追加该来源。
但"追加"有个容易走歪的地方:错的做法是把全局 default-src 或 img-src 放开成通配——为了缩略图把全站图片来源敞开;对的做法是按窗口分化策略。全局策略写一份基线,需要特殊放行的窗口在它自己加载的 HTML 入口里用 meta 标签追加窗口级条款——预览窗的入口页比基线多一条 img-src 的具体来源,主窗的入口页不动:
<!-- 预览窗入口页的 head:只追加预览需要的条款 --> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; connect-src 'self' ipc: http://ipc.localhost; img-src 'self' https://img.example-cdn.com">
分化的依据正是 6.1 的窗口信任域:预览窗本来就"接触外部内容",它多出的放行写它自己的入口页,不会传染给主窗。调试期还有一条经验法则:每修一条 CSP 报错,就问一次"这个放行能不能更窄"——能写具体域名就不写通配,能限图片就不放进 default-src。CSP 的维护成本主要在理解指令与资源类型的对应关系,值得把 Web 平台的指令速查表过一遍,Tauri 没有发明新语法,它只是替你把这份合同注入给 WebView。
壳与桥的边界锁完。下一节守住最后一环:出厂二进制的完整性——代码签名与公证。