【无标题】

一、前言

XSS(Cross-Site Scripting)是前端开发中最常见、也最容易被低估的一类安全问题。

在很多项目中,XSS 防护往往是零散的:有的写在组件里,有的写在工具函数里,有的干脆依赖"后端已经处理"。

随着项目复杂度上升,这种分散式处理方式几乎一定会失控。

最近在项目中,我对 XSS 做了一次系统性的收敛处理:在接口响应层统一对返回数据进行 XSS 清洗。

核心代码只有一行:return xssSanitizePayload(res.data);

但这行代码背后的工程化思路,才是这篇文章真正想讲的内容。

二、为什么"到处防 XSS"一定会出问题?

  1. 如果你在下面这些地方都做过 XSS 处理:

    • 接口返回后手动处理
    • computed / watch 中处理
    • v-html 使用前处理
    • 组件内部二次转义
  2. 那么你大概率遇到过这些问题:

    • 同一个字段在不同页面展示结果不一致
    • 正常 HTML 被误伤
    • 攻击 payload 变种后需要修改多个地方
  3. 本质原因只有一个:

    • XSS 防护没有收敛,规则分散在各处。
    • 安全逻辑一旦分散,维护成本和风险都会指数级上升。

三、XSS 真正危险的点,并不只是 script 标签

很多人对 XSS 的理解还停留在:

html 复制代码
<script>alert(1)</script>

但在真实攻击场景中,更常见的是:

html 复制代码
<img src=x onerror=alert(1)>
<a href="javascript:alert(1)">click</a>
<svg onload=alert(1)>
<div style="background:url(javascript:alert(1))"></div>

这些 payload 有一个共同点:它们并不依赖 script 标签。

因此,简单的字符串替换或正则拦截,本质上只能防"演示代码",而防不住真实攻击。

四、为什么选择在接口响应层统一处理?

我最终将 XSS 防护放在接口响应层,原因主要有三个。

  1. 数据入口足够集中:所有后端返回的数据,都会经过这一层,是天然的"统一入口"。
  2. 与业务逻辑彻底解耦:页面、组件、业务代码不需要关心任何安全细节,只关心数据本身。
  3. 规则可以统一维护:白名单、拦截策略、攻击特征升级,只需要改一处。

一句话总结:这是前端最适合做安全兜底的位置。

五、核心思路:白名单,而不是黑名单

在实现策略上,我没有选择"禁止一切",而是采用白名单机制。

  1. 明确禁止的内容

    • 危险标签: script、iframe、object、embed、svg、math 等
    • 危险属性:所有 on 开头的事件属性、style、srcdoc、formaction
    • 危险协议:javascript:、data:、vbscript:
  2. 明确允许的内容

    • 安全文本标签:a、p、span、br、ul、li、strong、em
    • 安全属性:href、title、target(并限制协议)

白名单的核心目标只有一个:允许"人类可读的 HTML",拒绝"浏览器可执行的代码"。

六、统一处理的示意实现

下面是一个简化版的示意实现,用于说明整体思路:

javascript 复制代码
function xssSanitizePayload(payload) {
  if (typeof payload === 'string') {
    return sanitizeString(payload);
  }

  if (Array.isArray(payload)) {
    return payload.map(item => xssSanitizePayload(item));
  }

  if (payload && typeof payload === 'object') {
    const result = {};
    Object.keys(payload).forEach(key => {
      result[key] = xssSanitizePayload(payload[key]);
    });
    return result;
  }

  return payload;
}

设计原则很明确:所有字符串,都是潜在的攻击入口。

七、为什么不用"全转义"方案?

很多人会问:直接把 < > " ' 全部转义,不就彻底安全了吗?

问题在于:你是否还需要展示富文本内容?

例如:用户评论,消息内容,公告说明,后台配置文案

全转义意味着这些功能直接不可用。

白名单方案的价值就在于:在安全性和可用性之间取得可控的平衡。

八、这个方案的边界在哪里?

需要明确的是,这个方案并不是"银弹"。

它主要解决的是:接口返回数据的 XSS 风险,富文本展示场景的前端兜底,前端侧统一、可维护的防护策略

它不能替代:后端输入校验,CSP(Content-Security-Policy),HttpOnly / SameSite Cookie,服务端模板转义XSS 永远是系统级问题,而不是某一层的责任。

九、总结

  • XSS 防护真正困难的地方,不在于技术本身,而在于工程化设计。
  • 将防护逻辑收敛到接口响应层
  • 使用白名单而不是简单字符串替换,可以让前端项目的安全性和可维护性同时提升一个量级。
相关推荐
程序员大阳4 小时前
副队长JavaScript教程(50)-DOM:网页开发的起点
javascript·dom·es·节点·概念·document·关系
香芋芋圆8 小时前
iD-editor 详解|OSM官方在线编辑器,基于D3.js的轻量地图编辑器
开发语言·javascript·编辑器
晗夜未央10 小时前
任务队列反复失败,先分清重试、阻塞和结果待核对
开发语言·javascript
jktantan11 小时前
每个节点 41 字节:把一棵百万节点树拆进 14 个 typed array
前端·javascript·vue.js·算法·typescript
我命由我1234511 小时前
访客系统形式(有用户概念、无用户概念)
java·开发语言·前端·javascript·python·产品运营·产品经理
程序员大阳12 小时前
副队长JavaScript教程(65)-详解JSON:轻量级数据交换格式
javascript·json·序列化·反序列化·stringify·parse
paopaokaka_luck14 小时前
基于springboot+vue的益暖微光公益服务平台(DeepSeek公益助手、善款信息追溯、志愿服务积分闭环、ECharts运营分析、活动状态自动流转)
java·前端·javascript·spring boot·mysql·架构·echarts
栀椩14 小时前
Electron 内嵌页面跨域问题(CORS/PNA)解决全记录
前端·javascript·electron
SEO_juper14 小时前
Chrome 扩展一键查看当前页面 SEO 信息:不用切页面,点一下全出来
前端·javascript·chrome
Highcharts.js14 小时前
CSUN 2026|Highcharts无障碍数据与AI分享
javascript·人工智能·数据分析·highcharts·图表开发