一、什么是 XSS?
XSS(Cross-Site Scripting,跨站脚本攻击)是指应用将不可信数据作为 HTML 或脚本处理,导致攻击者控制的代码在用户浏览器中执行。
其基本过程是:
不可信输入 → 进入页面 → 被浏览器作为代码解析 → 在当前站点上下文中执行。
不可信数据可以来自 URL 参数、表单、数据库、第三方接口、浏览器存储和跨窗口消息。
XSS 可能造成页面篡改、敏感信息读取,以及借助用户当前登录状态执行操作。具体影响取决于页面权限和可访问的数据。OWASP:XSS
二、反射型 XSS
2.1 产生原理
反射型 XSS 是指不可信输入随当前请求进入应用,随后未经适当处理,被带入响应页面并触发脚本执行。
例如,搜索页面读取 URL 中的关键词,并直接拼接到返回的 HTML 中。如果输入被浏览器识别为可执行内容,就可能产生 XSS。
其典型过程为:
携带恶意内容的请求 → 服务端生成响应页面 → 浏览器解析页面 → 执行恶意脚本。
2.2 常见场景
- 搜索结果页回显搜索关键词。
- 错误页面展示请求参数。
- 表单提交后回显用户输入。
- 页面根据 URL 参数显示提示信息。
2.3 主要特点
恶意内容通常不需要持久化保存,触发与特定请求有关。常见方式是诱导用户打开携带特定参数的链接,或提交特定请求。
三、存储型 XSS
3.1 产生原理
存储型 XSS 是指不可信内容被保存到数据库或其他存储中,之后在页面展示时被浏览器作为代码执行。
例如,评论内容被保存后,页面直接将评论当作 HTML 渲染。其他用户打开评论区时,就可能触发其中的恶意内容。
其典型过程为:
提交恶意内容 → 保存到存储系统 → 用户访问相关页面 → 内容被解析执行。
3.2 常见场景
- 评论区、论坛帖子和留言板。
- 用户昵称、个人简介和签名。
- 客服工单、反馈内容。
- 管理后台展示的日志和业务记录。
- 支持 HTML 的富文本内容。
3.3 主要特点
恶意内容具有持续性,一次提交可能影响多个后续访问者。
触发页面既可能是普通用户页面,也可能是管理员后台。风险取决于受影响用户的权限、访问人数及页面功能。OWASP:XSS
四、DOM 型 XSS
4.1 产生原理
DOM 型 XSS 是指前端 JavaScript 读取不可信数据后,将其传入 HTML 解析或代码执行接口,导致浏览器执行非预期内容。
其典型过程为:
前端读取不可信数据 → 拼接或转换数据 → 传入危险接口 → 浏览器解析执行。
例如,前端读取 URL 参数,再通过 innerHTML 将参数写入页面,就可能形成 DOM 型 XSS。
4.2 常见数据来源
- URL 查询参数:
location.search。 - URL 片段:
location.hash。 - 浏览器存储:
localStorage、sessionStorage。 - 接口返回的数据。
postMessage接收的消息。
4.3 需要重点关注的接口
- HTML 解析接口:
innerHTML、outerHTML、insertAdjacentHTML、document.write。 - 动态代码执行接口:
eval、new Function。 - 接收字符串代码的定时器调用。
这些接口接收不可信数据时,需要审查从数据来源到使用位置的完整路径。OWASP:DOM 型 XSS 防范
五、三种 XSS 的区别
| 对比项 | 反射型 XSS | 存储型 XSS | DOM 型 XSS |
|---|---|---|---|
| 核心特征 | 输入随当前请求进入响应 | 输入保存后在后续访问中展示 | 前端将数据交给危险接口 |
| 常见来源 | URL 参数、表单、请求路径 | 评论、昵称、工单、日志 | URL、浏览器存储、接口、消息 |
| 是否需要保存 | 通常不需要 | 需要某种存储 | 可有可无 |
| 常见触发方式 | 打开特定链接或提交请求 | 访问包含相关内容的页面 | 前端读取数据并执行危险操作 |
| 重点关注位置 | 服务端响应模板 | 数据保存后的所有展示入口 | 前端数据流和 DOM 操作 |
| 脚本执行位置 | 用户浏览器 | 用户浏览器 | 用户浏览器 |
反射型和存储型描述的是数据的传递方式,DOM 型描述的是客户端处理机制。因此,这些特征可以同时出现:数据库中的评论被前端通过 innerHTML 插入页面,就可能同时具有存储型和 DOM 型特征。OWASP:XSS 分类
六、XSS 与原型链污染的关系
6.1 什么是原型链污染?
JavaScript 对象读取属性时,如果对象自身没有该属性,就会沿原型链继续查找。
原型链污染(Prototype Pollution)是指应用在处理不可信数据时,允许攻击者修改原型对象上的属性,使其他对象在读取继承属性时获得攻击者控制的值。
常见风险入口包括不安全的深度合并、动态属性赋值和嵌套参数解析。是否影响某个对象,取决于它的原型链以及自身是否已经定义了同名属性。MDN:原型链污染
6.2 原型链污染如何引发 XSS?
原型链污染可以成为 XSS 的上游入口:它先影响应用读取到的数据或配置,再由后续页面逻辑将这些值传入危险接口。
典型过程为:
不可信输入 → 污染原型属性 → 应用读取被污染的属性 → 数据进入 HTML 解析或脚本执行接口 → 触发 XSS。
例如,页面从配置对象读取一段 HTML。如果该对象没有定义对应属性,而它的原型上存在被污染的同名属性,页面就可能读取到攻击者控制的内容。随后将其传入 innerHTML 等接口,就可能形成 DOM 型 XSS。
因此,原型链污染能否转化为 XSS,取决于两个条件:
- 攻击者能够通过应用的数据处理流程修改原型属性。
- 后续代码会读取这些属性,并将其用于可以触发脚本执行的位置。
只有污染入口,而没有后续可利用的数据流,并不必然产生 XSS。MDN:原型链污染
七、XSS 的防范方式
XSS 防范的核心,是让不可信数据始终按照预期用途处理。普通文字应作为文本展示,富文本应经过安全清洗,链接应检查协议与业务范围,用户输入应避免进入动态代码执行过程。
防护需要落实到最终使用位置。即使数据来自自己的数据库或接口,也应根据它进入的是 HTML、属性、URL 还是其他上下文,选择对应的处理方式。前端与后端应分别保护自身负责的渲染入口。OWASP:XSS 防范
| 防范方式 | 对应 XSS 类型 | 前后端 |
|---|---|---|
| 根据输出上下文进行编码,保持模板自动转义开启 | 反射型、存储型;客户端模板场景下的 DOM 型 | 前端、后端 |
普通文字使用 textContent、文本节点或框架默认文本绑定 |
DOM 型,以及由前端渲染的反射型、存储型 | 前端 |
| 富文本使用成熟的 HTML 清洗库,限制允许的标签和属性 | 三种类型 | 前端、后端 |
避免不可信数据进入 innerHTML、document.write 等 HTML 解析接口 |
DOM 型,包括与反射型、存储型重叠的场景 | 前端 |
避免使用 eval、new Function 和字符串形式的定时器处理外部数据 |
DOM 型 | 前端 |
| 对动态链接进行 URL 解析、协议白名单和必要的域名校验 | 三种类型 | 前端、后端 |
| 按业务规则校验输入的类型、长度、格式和取值范围 | 三种类型的辅助防护 | 后端强制校验,前端配合 |
对 postMessage 校验发送方来源、消息结构及必要的窗口身份 |
DOM 型 | 前端 |
| 配置 CSP,限制脚本来源和执行方式 | 三种类型的纵深防护 | 后端配置响应头,前端配合 |
| 持续更新前端框架、模板引擎和 HTML 清洗库 | 三种类型 | 前端、后端 |
| 审查数据来源到输出位置的完整路径,并覆盖后台和预览页面 | 三种类型 | 前端、后端 |
对嵌套输入实施结构和字段白名单校验,防止动态写入沿 __proto__、constructor.prototype 等路径修改原型 |
原型链污染引发的 XSS,重点是 DOM 型 | 前端、后端 |
字典数据优先使用 Map 或无原型对象;读取关键配置时使用 Object.hasOwn() 判断自身属性 |
原型链污染引发的 XSS,重点是 DOM 型 | 前端、后端 |
| 持续更新深度合并、参数解析等依赖,并审查不可信对象的合并流程 | 原型链污染引发的 XSS | 前端、后端 |
富文本清洗应使用持续维护的组件,并避免在清洗之后再次拼接未经处理的内容。DOMPurify 官方文档
CSP 可以增加脚本执行限制,应与安全输出、文本接口和 HTML 清洗共同使用。OWASP:CSP