前端安全
前端安全的第一性原则:浏览器里的一切都不可信——代码、存储、环境都可能被攻破或篡改,所以"密钥/权限判断"绝不放客户端,安全以服务端为权威。但前端仍然有自己必须守住的防线:XSS(脚本注入)是第一大敌,供应链(依赖投毒)是 2020s 最凶险的攻击面。本文按攻击类型逐一给出防御矩阵,最后给一套可执行的落地清单。
一、威胁模型:前端在防谁、防什么
| 攻击 | 攻击面 | 后果 | 前端角色 |
|---|---|---|---|
| XSS(存储/反射/DOM 型) | 用户输入、URL 参数、富文本、第三方内容 | 窃取会话、钓鱼、篡改页面 | 主要防线 |
| CSRF | 跨站伪造请求(cookie 自动携带) | 以用户身份执行操作 | 配合防线 |
| 供应链投毒 | 依赖、构建工具、发布源 | 植入后门、泄露数据 | 可防御 |
| 数据泄露 | 密钥入前端、日志、水合数据 | 直接泄露 | 红线:不在前端放机密 |
| 点击劫持/钓鱼 | iframe 透明覆盖 | 诱导误操作 | 头部防护 |
二、XSS:三个入口、同一套编码纪律
2.1 三种注入时机
| 类型 | 触发 | 示例路径 |
|---|---|---|
| 反射型 | 恶意输入经 URL 带回页面 | 搜索词回显 <script> |
| 存储型 | 恶意输入落库、他人访问时渲染 | 评论区、昵称、富文本 |
| DOM 型 | 前端用恶意数据写 DOM | innerHTML、location.hash、eval |
2.2 防线一:框架默认转义(不要主动破坏它)
React/Vue/Svelte 默认把插入文本当字符串转义,这是第一道闸:
jsx
// React:{expr} 默认转义 —— 安全
<p>{user.comment}</p>
// ❌ 危险:跳过转义注入 HTML(数据必须经白名单清洗后才可到这里)
<p dangerouslySetInnerHTML={{ __html: sanitize(html) }} />vue
<!-- Vue:{{ }} 插值默认转义 -->
<p>{{ comment }}</p>
<!-- ❌ v-html 等于放弃转义 -->
<p v-html="comment"></p>铁律:
innerHTML/v-html/dangerouslySetInnerHTML/outerHTML/document.write/eval是 XSS 的六扇门。团队规范应把"非受控 HTML 注入"设为 code review 重点 + lint 规则(如vue/no-v-html)。
2.3 防线二:编码上下文要对应
"转义"必须匹配输出上下文,否则无效:
| 输出位置 | 需要的编码 |
|---|---|
| HTML 元素内容 | HTML 实体编码 |
| HTML 属性(引号内) | 属性编码(实体 + 引号) |
| JS 字符串 | JS 字符串转义 |
| URL(href/src) | 协议白名单(禁止 javascript:) |
| CSS | 避免拼接,用变量/工具 |
2.4 防线三:白名单清洗(富文本场景)
富文本不能靠"转义"(会破坏格式),要白名单标签/属性清洗:
js
import DOMPurify from 'dompurify'
const clean = DOMPurify.sanitize(rawHtml, {
ALLOWED_TAGS: ['p', 'b', 'a', 'img', 'ul', 'li'],
ALLOWED_ATTR: ['href', 'src', 'alt'],
})
// 用 clean 替换原值后再注入;禁止"黑名单"思路(漏一个就完)2.5 防线四:CSP 兜底(纵深防御)
即使注入发生,CSP 也能挡掉执行:
http
Content-Security-Policy:
default-src 'self';
script-src 'self'; /* 非内联非 eval,违规直接不执行 */
img-src 'self' data: https:;
style-src 'self' 'unsafe-inline'; /* 内联样式常需放宽,最小化 */
frame-ancestors 'none'; /* 顺带防点击劫持 */
object-src 'none';- 内联脚本/CSS 会破坏 CSP——Vite/Webpack 构建产物应外链 + 指纹,开发环境单独放宽;
- 需要
nonce/hash才放行个别内联:脚本上带nonce属性并由服务端每次下发随机值; - 全站默认值先"观察模式"(
Content-Security-Policy-Report-Only)收集违规再收紧。
三、CSRF 与登录态
原理:浏览器会自动携带站点 cookie,恶意站 fetch/post 到目标站即以用户身份执行。
| 防线 | 说明 |
|---|---|
SameSite=Lax/Strict(cookie 属性) | 现代首选:跨站请求不带 cookie;Lax 兼容跳转导航 |
| CSRF Token | 页面下发随机 token,请求头带上,服务端校验(自定义头 X-CSRF-Token 也可防表单伪造) |
| 自定义请求头 + CORS | 要求 X-Requested-With 等自定义头(触发预检),服务端 CORS 白名单不放行任意源 |
| 二次验证 | 高敏操作(改密/转账)要求验证码或重新登录 |
js
// fetch 默认不带跨站凭证;同站也要显式管理 credentials
fetch('/api/order', {
method: 'POST',
credentials: 'same-origin', // 别误开 include 到不可信域
headers: { 'X-CSRF-Token': csrfToken }, // token 走自定义头而非 cookie
})现代 API 若只用
SameSite=Lax+ 同源 JSON 基本已挡 CSRF;仍要防的是:老系统 cookie、GET写操作、第三方登录回跳。
四、供应链安全:依赖即攻击面
近年的教训:流行包被投毒、构建脚本偷环境变量、伪装包名抢注。防御矩阵:
| 措施 | 做法 |
|---|---|
| 锁定依赖 | 提交 package-lock.json/pnpm-lock.yaml(锁传递依赖与解析树),CI 用 --frozen-lockfile |
| 自动化扫描 | CI 接入 npm audit(或 pnpm audit)/ osv-scanner / GitHub Dependabot 定时扫漏洞并开 PR |
| 源头与身份 | 只装官方 registry;package.json 里校验发布源;关注包名是否"抢注变体"(lodahs vs lodash) |
| 最小安装 | npm install --omit=dev 区分运行时依赖;devDependencies 的脚本同样可被利用 |
| 签名与 SBOM | 发布物可校验签名;生成 SBOM 清单便于漏洞通报后自检 |
| 变更可审计 | 核心依赖锁版本、升级走 review;对"安装后执行脚本"(postinstall)包重点审查 |
sh
# CI 里卡漏洞门禁(高危拒绝)
npm audit --omit=dev --audit-level=high
pnpm audit --prod --audit-level=high别把
npm audit的每个中危都当敌人(会噪音疲劳),要设策略:生产依赖高危必修、开发依赖中危限期、上线前npm audit signatures(校验包签名,2022+ registry 能力)跑一遍。
五、数据面:密钥与水合
- 前端无秘密:API key、JWT 密钥、签名私钥、数据库口令一律不进前端代码与环境变量(前端 env 会被构建产物完整暴露);
- 登录态存储:HttpOnly + Secure + SameSite cookie 放会话;
localStorage存 token 会被任何同源 XSS 直接偷走——尽量 cookie,或短期内存 + 刷新续期; - SSR 水合泄漏:水合数据(
__INITIAL_STATE__、window.__DATA__)别带敏感字段,按需裁剪,别整表序列化; - 日志与上报:前端日志/Sentry 会出境——上报前去敏感字段(token/身份证/密码),本地 console 别打印请求体;
- Referrer 泄漏:含 query token 的 URL 别被 Referrer 带出去——
Referrer-Policy: strict-origin-when-cross-origin。
六、现代 Web 安全 API 补强
| API/头部 | 作用 |
|---|---|
integrity="sha384-..."(SRI) | 外部脚本/CSS 校验哈希,防 CDN 被篡改 |
| Trusted Types | 强制"注入 DOM 的字符串必须经策略创建",消灭大部分 DOM XSS |
Permissions-Policy | 关闭不需要的浏览器能力(如 geolocation=()) |
Referrer-Policy | 见 §5 第 5 点 |
X-Frame-Options: DENY / frame-ancestors | 防点击劫持 |
Cross-Origin-Opener-Policy / Embedder | 侧信道(Spectre)缓解,同源窗口隔离 |
X-Content-Type-Options: nosniff | 防 MIME 嗅探 |
七、常见坑速查
- "框架会转义所以不用管":只对默认文本插值成立,
v-html/dangerouslySetInnerHTML/innerHTML一开闸就全裸(§2.2 铁律); - 富文本用黑名单过滤:
replace(/<script>/g, '')之类——绕过方式几十种,必须 DOMPurify 白名单(§2.4); - 内联事件/
javascript:URL:<a href={user.link}>未校验协议 →javascript:alert(1);URL 一律协议白名单(http/https/mailto…)并转义; - 密钥塞前端 env:
VITE_XXX全部进产物——任何"前端密钥"命题都反了(§5.1); - token 放 localStorage:同源一处 XSS 即全量失守(§5.2);
- 锁定文件不提交 / CI 不用 frozen:依赖悄悄漂移,昨天能跑今天被投毒都不知道;
- CSP 一步到位直接禁:先 Report-Only 收集,否则上线当天各种外链被拍死(§2.5);
- 只在"登录后页面"做安全:攻击面是整站——静态页、404、错误页同样要有
Security Headers与输出转义; - 忽略第三方脚本:埋点/客服小窗/广告 SDK 拥有你同源 JS 权限——引入前评估其 CSP 与权限,定期审查名单。
状态与参考
- 状态:已收录(2026-09-02,由前端领域规划清单「前端安全|XSS、CSRF 与依赖供应链安全」转正式)。
- 版本与声明:本文按 2026 年浏览器安全基线编写(SameSite/Trusted Types/CSP3 等为现代浏览器稳定能力);头部配置与库用法以官方文档为准,示例为标准配置可直接照抄后调整白名单。
- 参考:OWASP(含 XSS 防御速查)、MDN:CSP、DOMPurify、OWASP Dependency-Check(供应链)、web.dev 安全系列。
- 阅读联动:渲染管线侧的存储与防线综述见浏览器原理(六种存储、CSP/SRI 一节);SSR/水合与产物相关见 Astro 与 构建工具;登录态实践见项目部署。
下一步
- [ ] 用一个 Vite 工程搭"默认安全基线"模板:CSP 头部、SRI、DOMPurify 封装、审计脚本、环境变量红线检查
- [ ] 写一篇 OWASP Top 10 在"当前团队技术栈"上的逐项对照自查表
- [ ] 用开源靶场(如 DVWA/DVGA)实操一遍三种 XSS 与 CSRF 复现,沉淀笔记
写作规范请参阅领域概览。