6.3 CSP配置与XSS、RCE防护


门禁管的是"进去之后能干什么",CSP 管的是"恶意代码根本进不来"。本节讲内容安全策略的字段语义、Tauri 的自动 nonce 注入,然后把 XSS 到 RCE 的完整攻击路径走一遍,标出每一段的断点在哪。读完本节,你能对"注入"这个词给出工程级的防守方案,而不是一句"要防 XSS"。

CSP 是什么:页面的白名单合同

内容安全策略是一串声明,告诉 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 的额外放行,脚本的来源被钉死在自身。

自动 nonce:解决"内联脚本"的死结

严格的 CSP 禁止内联脚本(script-src 不给 unsafe-inline),但 Tauri 自己要往页面注入初始化脚本——IPC 的桥就是注入进去的。两难:放开内联,注入通道大开;收死内联,框架自己跑不了。Tauri 的解法是 nonce 机制:构建时给注入脚本生成一次性随机标记,并把它追加进 CSP 的 script-src——于是"只有带这个随机标记的内联脚本"被放行,攻击者注入的脚本没有标记,照样被拦。这套机制默认开启且自动维护(你写的 CSP 字符串会被追加标记);与之配套的 dangerousDisableAssetCspModification 选项能关掉这个行为——别关,名字里的 dangerous 就是给你看的。

图 6-3:XSS 到 RCE 的路径与断点

图 6-3:XSS 到 RCE 的路径与断点

走一遍攻击路径:三段断点

把最坏假设展开成四段路径,逐段看锁在哪:

第一段,恶意内容进入。 粘贴板、导入文件、第三方依赖都可能带恶意标记。此段的防线是入口净化——凡是要以 HTML 形式渲染的外来内容,先过净化库把脚本类节点剥干净;纯文本渲染(textContent 而非 innerHTML)是零成本的默认选择。

第二段,脚本执行(XSS 成立)。 就算净化漏了、脚本进了页面——CSP 的 nonce 与 self 钉死让它大概率跑不起来。这是断点一。注意"大概率":CSP 是纵深防御的一层,不是绝对屏障,别把它当唯一防线用。

第三段,冒充前端过桥。 脚本跑起来了,开始调命令——权限闸门按窗口授权集合放行,6.2 里"能少授就少授"的每一项节约,在这里变成攻击者的每一次碰壁。这是断点二。

第四段,系统能力失守(RCE)。 假设授权集合里确有 fs 读权限,脚本传进来的是精心构造的越界路径——5.3 的 ensure_within 与第 6.2 的作用域在此合围。参数校验是断点三,也是芯侧的最后一道。

四段推演的结论是纵深防御:三个断点互相独立,任何一层失守不引发连锁。安全审查时逐层自问"CSP 被绕过会怎样、权限被滥用会怎样、校验被跳过会怎样",三问都有答案,才叫有防线。

调试 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。

收紧清单

  • CSP 保持 self 加 ipc 最小集,每一项新增来源都要有明确理由;
  • 永不使用 unsafe-inline 的 script-src;样式按需才放开;
  • 不关自动 CSP 修改(nonce 依赖它);
  • 外来 HTML 必净化,纯文本渲染优先;
  • 远程页面不进主窗:确需加载外部内容时单开窗口、最小授权、默认不给任何核心权限。

本节要点回顾

  • CSP 是白名单合同:default-src self 是底线,connect-src 记得放行 ipc 通道;
  • nonce 自动注入解决框架自身注入与内联禁令的两难,配套开关别动;
  • 攻击路径四段三断点:净化、CSP、权限闸门、入参校验,纵深独立;
  • 远程内容单窗隔离:主窗不加载外部页面;
  • 安全审查三问:CSP 被绕、权限被滥、校验被跳,逐层有答案。

壳与桥的边界锁完。下一节守住最后一环:出厂二进制的完整性——代码签名与公证。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U