XSS 攻击防方法详解(Vue 前端纵深防御实战)

前端防 XSS 的核心结论:Vue 的模板插值默认转义已经挡掉大部分注入,真正的风险集中在三个逃生窗口——渲染富文本、绑定动态链接、拼接不可信内容进 DOM。我们在企业级 AI 网关项目的对话页和管理页里,用”默认转义 + 白名单净化 + 协议校验 + CSP 兜底”四层防御,把 AI 生成内容和用户输入都当作不可信数据处理。落地顺序是:能不用 v-html 就不用,非用不可先过净化器,动态链接先验协议,最后用内容安全策略兜住意外穿透。

一、先认清 Vue 里哪些位置会漏

1.1 默认防护到底覆盖了哪里

Vue 的模板插值语法会自动对内容做 HTML 转义,渲染出来的永远是纯文本,这是框架层面的第一道防线。动态属性绑定同样会被转义,但属性绑定不会拦截恶意协议,这是很多人忽略的分界点。

渲染方式 是否转义 主要风险
模板插值(双花括号) 转义 基本无风险
文本节点与属性绑定 属性值转义 协议注入风险
v-html 指令 不转义,原样渲染 高风险,必须净化
自定义指令内直接操作 innerHTML 不转义 同 v-html

1.2 三个最容易出问题的场景

AI 网关项目里风险最集中的是三处:对话正文中的 Markdown 渲染(AI 生成内容里可能夹带构造好的 HTML 标签)、模型配置页的富文本描述、以及用户提交的链接地址。这三处的共同点都是”不可信内容要进 HTML 上下文”。

二、四层防御的落地代码

2.1 第一层:默认转义不主动关掉

对话页的纯文本消息一律用插值渲染,保持框架默认转义。只有确认需要富文本展示时才走净化流程,不为了一点点排版便利放弃默认防线。

2.2 第二层:白名单净化器统一收敛

所有需要渲染 HTML 的地方,统一走一个 useSafeHtml 组合式函数,内部用 DOMPurify 按白名单过滤。白名单只放基础排版标签,属性只放 href 等必要项,其余一律剥离。

// composables/useSafeHtml.ts
import DOMPurify from 'dompurify'

const ALLOWED_TAGS = ['p', 'br', 'strong', 'em', 'ul', 'ol', 'li', 'a', 'pre', 'code']
const ALLOWED_ATTR = ['href', 'target', 'rel']

export function useSafeHtml() {
  function sanitize(raw: string): string {
    return DOMPurify.sanitize(raw, {
      ALLOWED_TAGS,
      ALLOWED_ATTR,
      ALLOW_DATA_ATTR: false,
    })
  }
  return { sanitize }
}

对话正文的 Markdown 渲染同样要先过这道净化。我们禁用了 Markdown 渲染器里的原生 HTML 透传选项,让所有输出先落在白名单里再进 DOM。

2.3 第三层:动态链接先验协议

属性绑定不拦截恶意协议,因此对用户可控的 URL 单独做一次协议校验。合法协议只放 http、https、mailto,其余直接降级为空链接。

// utils/safeUrl.ts
const ALLOWED_PROTOCOLS = ['http:', 'https:', 'mailto:']

export function safeUrl(raw: string): string {
  try {
    const parsed = new URL(raw)
    return ALLOWED_PROTOCOLS.includes(parsed.protocol) ? raw : '#'
  } catch {
    return '#'
  }
}

用在新开窗口的链接上时,还要补上 rel="noopener noreferrer",防止被引用页面通过 window 对象反向控制原页面。

2.4 第四层:CSP 兜底

净化器只能保证已知载荷不生效,CSP 负责挡住未知变体。网关前端构建后在服务端返回内容安全策略响应头,限制脚本来源,生产构建下 Vue 3 不使用 eval 和动态编译,可以放心收紧:

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self'

connect-src 需要按实际 API 域名放行,SSE 长连接同源即可,跨域时单独加白。

2.5 富文本编辑器的粘贴入口

模型配置页如果接富文本编辑器,粘贴动作是漏网高发区。编辑器把剪贴板里的带格式内容转成 HTML 再进 v-html 链路,粘贴进来的内容往往自带内联事件属性。我们统一在粘贴事件里先做一次文本清洗,剥掉事件属性后再交给净化器,双重过滤比单靠运行时净化更稳。

三、管理页面容易忽略的两个点

模型配置页的表单校验只拦了格式,没拦协议。名称、描述、系统提示词这类字段的输入长度要有上限,链接字段在保存前用同一个 safeUrl 校验,前端和网关各验一遍,防止绕过前端直接调接口。路由参数同理,页面挂载时对路由 query 和 param 做类型校验,不符合预期格式直接跳 404,避免把异常值带进页面状态。

四、上线前的自检清单

  1. 用 Grep 搜一遍代码库里的 v-html 指令,逐个确认是否经过净化器;
  2. 对所有用户可控的链接绑定点,注入测试载荷并确认被降级为空链接;
  3. 在对话输入框粘贴构造过的富文本内容,确认渲染结果为纯文本或被剥离;
  4. 用浏览器开发者工具检查响应头,确认 CSP 已生效且未放行外部脚本域。

常见问题(FAQ)

Q1:净化器会误伤正常排版吗?

白名单覆盖常见排版标签,超出白名单的样式建议改用 Markdown 而非原始 HTML。

Q2:CSP 会不会影响 SSE 长连接?

同源请求不受影响,跨域需在 connect-src 中显式放行网关域名。

Q3:AI 生成内容也需要净化吗?

需要。模型输出不可信,同样按用户输入标准走白名单净化。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 qiqicto@qq.com 举报,一经查实,本站将立刻删除。
赞 (0)
蓝队管理员的头像蓝队管理员普通用户

相关推荐

返回顶部