引言
前端安全经常被忽视——直到线上被打穿。Vite 项目的基础设施(构建、依赖、产物)本身就构成攻击面:依赖被投毒、构建脚本被篡改、产物泄漏源码、内联脚本被注入。本文不重复「别写 XSS」的老话,而是聚焦构建层与配置层的安全加固:CSP 的正确配置(含 nonce)、依赖供应链的锁与扫、产物的敏感信息治理,以及开发/生产两套环境的策略差异。目标是让「安全」成为 Vite 工程的一部分,而不是事后补丁。
前置:https://plumephp.com/vite-env-production-best-practices/(生产构建)、https://plumephp.com/vite-config-guide/(配置)、https://plumephp.com/vite-sourcemap-deep-dive/(产物安全)。
目录
- 1. 前端攻击面与构建层安全
- 2. CSP 指令体系
- 3. nonce 与 hash 机制
- 4. Vite 下的 CSP 配置
- 5. 开发环境与生产的差异
- 6. 依赖供应链安全
- 7. 构建产物敏感信息治理
- 8. XSS 与注入缓解
- 9. 安全基线与自动化
- 10. 速查表与一句话记忆
- 延伸阅读
1. 前端攻击面与构建层安全
Vite 前端项目的攻击面远不止「用户输入」:
攻击面 1:依赖供应链(npm 投毒、恶意包)
攻击面 2:构建脚本(postinstall 执行任意代码)
攻击面 3:产物(源码泄漏、API key、内联注入)
攻击面 4:运行时(XSS、CSRF、点击劫持)← CSP 主要防护
安全前置原则:
- 构建是「信任边界」:构建产物会被公开部署,任何泄漏都不可撤回;
- 依赖即代码:
node_modules里的每一行都可能在你的应用里执行; - CSP 是纵深防御的一层:堵住「即使 XSS 发生,也拉不起外部载荷」。
安全顺序:供应链锁 → 构建治理 → CSP 兜底 → 运行时校验
2. CSP 指令体系
CSP(Content Security Policy) 通过响应头/<meta> 告诉浏览器「允许从哪加载什么」。核心指令:
| 指令 | 控制 | 典型值 |
|---|---|---|
default-src | 所有未显式指定的类型 | 'self' |
script-src | 脚本来源 | 'self' 'nonce-xxx' |
style-src | 样式来源 | 'self' 'unsafe-inline'(样式常需) |
img-src | 图片来源 | 'self' data: |
connect-src | 网络请求(fetch/XHR/WS) | 'self' https://api.x.com |
frame-src | 可嵌入的 iframe | 'none' 或白名单 |
object-src | 插件/object | 'none' |
base-uri | <base> 标签 | 'self' |
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-aBc123'; style-src 'self'; img-src 'self' data:; connect-src 'self' https://api.example.com; object-src 'none'; base-uri 'self'
铁律:
- 绝不写
'unsafe-inline'在script-src——那等于 CSP 形同虚设; connect-src要收口:只允许必要的 API 域名;object-src 'none'+base-uri 'self':堵住最便宜的 XSS 载体。
3. nonce 与 hash 机制
内联脚本(<script>...</script>)在 script-src 'self' 下会被禁止。但构建工具(包括 Vite 的 dev 注入、某些 SSR)会生成内联脚本。解决方案是 nonce(每次响应随机的一次性 token)或 hash:
<!-- nonce 方案:服务器为每个响应生成随机 nonce -->
<script nonce="aBc123">
// 只有带正确 nonce 的内联脚本才被允许
</script>
<!-- hash 方案:把脚本内容的 sha256 白名单化 -->
Content-Security-Policy: script-src 'sha256-<BASE64_HASH>'
nonce 注意:
✓ 每次响应重新生成(不可复用)
✓ 只加在「信任的」内联脚本上
✗ 绝不能通过脚本动态设置 CSP(会被绕过)
Vite 结合:SSR 场景下由服务器为每个 HTML 响应生成 nonce 并注入 script-src;纯 SPA + 外部脚本时,通常只需要 'self' + 少量 sha256- 即可。
4. Vite 下的 CSP 配置
在 Vite 项目里落地 CSP 有两种方式:
方式 A:静态响应头(CDN/网关/nginx 配置)
└─ 适合:构建产物不变的纯 SPA
方式 B:HTML 注入(Vite 插件 / SSR 生成时)
└─ 适合:需要 nonce、需要按请求动态生成
// 方式 B 示例:Vite 插件为 html 注入 CSP meta
export function cspPlugin(policy: string): Plugin {
return {
name: "inject-csp",
transformIndexHtml(html) {
return {
html,
tags: [
{ tag: "meta", attrs: { "http-equiv": "Content-Security-Policy", content: policy }, injectTo: "head-prepend" },
],
};
},
};
}
// vite.config.ts
plugins: [
cspPlugin([
"default-src 'self'",
"script-src 'self' 'nonce-__NONCE__'",
"style-src 'self' 'unsafe-inline'", // CSS 常需 inline(style 属性)
"connect-src 'self' https://api.example.com",
].join("; ")),
],
工程要点:
style-src常用'unsafe-inline':Vue/React 的 style 属性与 CSS-in-JS 依赖它,折中可接受;connect-src与 dev 代理:开发时server.proxy转发 API,connect-src只需'self'(同源代理);- 上线前用
CSP Evaluator检查策略:避免配错导致功能被误杀。
5. 开发环境与生产的差异
CSP 在开发与生产应该有不同策略:
| 场景 | 建议 | 原因 |
|---|---|---|
| 开发 | 放宽或关闭 CSP | @vite/client、HMR 需注入脚本与 WS |
| 生产 | 严格 CSP | 真防护、不拖累功能 |
| 测试 | 与生产一致 + 记录 violation | 尽早暴露「CSP 杀功能」 |
开发时常见「CSP 冲突」:
- HMR WebSocket 被 connect-src 拦 → 需加 ws://localhost:*
- @vite/client 内联脚本被 script-src 拦 → dev 放宽或加 nonce
→ 用 Vite 插件按 mode 区分 policy 即可
工程要点:CSP 应在测试环境先严格生效,收集 violation 报告(report-uri/report-to)观察「误杀」,再在生产全量启用——不要一上线就严格,否则一堆功能被悄悄禁用。
6. 依赖供应链安全
供应链攻击(npm 包被植入恶意代码)是前端最高危的威胁。加固手段:
# 1. 锁版本:commit lockfile,杜绝「下次装到不同版本」
# pnpm-lock.yaml / package-lock.json / yarn.lock
# 2. 漏洞扫描
npm audit / pnpm audit
# 3. 许可证与来源检查
npm ls --all | grep "pinned"
# 4. CI 强制审计
CI 里把 audit 结果设为门禁(fix:true 时失败)
// 显式锁定传递依赖(防被替换):
"overrides": {
"lodash": "^4.17.21", // 强制所有包用安全版本
"tar": "6.2.1"
}
供应链安全清单:
□ lockfile 进版本库、CI 用 frozen-lockfile 安装
□ 定期 pnpm audit,高危漏洞设修复时限
□ 关注 deprecation / abandoned 的包
□ 禁止直接依赖「月下载量个位数」的可疑包
□ postinstall 脚本审计(npm 允许包自带脚本执行)
7. 构建产物敏感信息治理
Vite 构建产物是「公开部署的文件」,常见的泄漏:
| 泄漏 | 来源 | 治理 |
|---|---|---|
| API key / 私钥 | 代码里硬编码 | 环境变量 + import.meta.env(仅 VITE_ 前缀进产物) |
| 源码 | source map 公开 | hidden + map 不部署(https://plumephp.com/vite-sourcemap-deep-dive/) |
| 内部 URL | 注释/日志 | 构建前清理敏感注释 |
| 调试开关 | console.log 泛滥 | 生产移除或降级 |
// 只有 VITE_ 前缀的环境变量会进产物!
// 服务端密钥绝不进 import.meta.env(用 .env.server 或后端注入)
import.meta.env.VITE_PUBLIC_API // ✅ 进产物
process.env.SERVER_SECRET // ❌ 绝不可出现在前端
治理纪律:
- 前端永远没有秘密:所有「前端要用的密钥」都要假定会公开,改用后端代理/签名;
- 构建产物扫描:CI 里 grep 产物是否含密钥模式(
sk-、AKIA等)→ 门禁; - source map 治理:见第 8 节/第 9 节的 hidden 策略。
8. XSS 与注入缓解
CSP 是「最后一道网」,配合运行时防御:
防 XSS 的层次:
1. 输入:验证 + 净化(不信任任何外部数据)
2. 输出:框架默认转义(React/Vue 默认安全)
3. 兜底:CSP 禁止执行未授权脚本
4. 保险:v-html / dangerouslySetInnerHTML 白名单化
// 需要渲染富文本时:净化 + CSP 双重保险
import DOMPurify from "dompurify";
const safe = DOMPurify.sanitize(userHtml); // 剥掉 script/事件属性
Vite 相关的注入点:
index.html模板注入:如果transformIndexHtml拼接了用户输入,要转义;- 动态
import():拼接路径来自外部 → 路径穿越/原型污染; dangerouslySetInnerHTML/v-html:项目内 grep 排查,禁止直接渲染不可信 HTML。
9. 安全基线与自动化
把安全变成「CI 自动门禁」,而不是「人工想起来」:
安全流水线(示例):
□ pnpm audit 高危=0(门禁)
□ 供应链扫描(Snyk/Dependabot)
□ 产物敏感信息扫描(grep 密钥/URL)
□ source map 是否泄漏(扫描 public .map)
□ CSP 策略语法校验 + violation 监控
□ 依赖更新 PR 自动触发安全评审
// package.json 脚本
"security:audit": "pnpm audit --audit-level=high",
"security:scan": "node scripts/scan-dist.mjs",
"ci": "pnpm security:audit && pnpm security:scan && pnpm build"
工程要点:安全基线要可度量、可门禁——audit 高危及以上的数量归零、产物扫描通过、CSP violation 监控在阈值内。没有指标的安全承诺都是口号。
10. 速查表与一句话记忆
| 问题 | 一句话答案 |
|---|---|
| CSP 管什么 | 浏览器允许从哪加载脚本/样式/请求 |
| script-src 铁律 | 不写 'unsafe-inline',用 nonce/sha256 |
| Vite 怎么配 CSP | 插件注入 meta 或网关加响应头 |
| dev 与 prod 差异 | dev 放宽(HMR/WS),prod 严格 + report |
| 供应链怎么防 | lockfile + audit + overrides + CI 门禁 |
| 产物泄漏什么 | 密钥(VITE_ 前缀)、source map |
| XSS 兜底 | CSP + DOMPurify + 框架默认转义 |
一句话记忆:前端安全 = CSP 收口(script/connect/object 三件套 + nonce)+ 供应链锁(lockfile/audit/overrides)+ 产物治理(密钥不进前端/sourcemap hidden)——把安全做进构建与依赖层。
延伸阅读
- https://plumephp.com/vite-env-production-best-practices/ — 环境变量与生产构建
- https://plumephp.com/vite-sourcemap-deep-dive/ — source map 安全与 hidden 策略
- https://plumephp.com/vite-plugin-development/ — transformIndexHtml 与插件
- https://plumephp.com/vite-ssr-frameworks/ — SSR 的 CSP nonce 注入
- 安全专题 — Web 应用安全与审计
- DevOps 专题 — CI 安全门禁与供应链
- Node.js 专题 — 依赖管理与锁文件
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。