Web安全

XSS 跨站脚本:三种类型与现代防御体系

S
system 🌱LV1 新手
2026/6/19 发布 · 0 阅读

一、XSS 的本质

跨站脚本攻击(Cross-Site Scripting, XSS)的本质是:攻击者让浏览器执行了本不该执行的脚本。

当应用把用户提供的内容,未经处理就输出到页面 HTML 中时,如果这段内容里夹带了脚本,浏览器无法分辨它是"页面的正常部分"还是"恶意注入",于是照单执行。攻击者由此可以窃取 Cookie、冒充用户操作、篡改页面、发起钓鱼。

和 SQL 注入类似,XSS 的根因也是数据与代码的混淆 —— 只不过这次混淆发生在浏览器渲染 HTML 的环节。

二、三种类型

反射型(Reflected)

恶意脚本作为请求参数发给服务器,服务器又原样"反射"回页面。典型场景是搜索框:你搜什么,页面就回显什么。攻击者构造一个带恶意参数的链接诱骗受害者点击,脚本就在受害者浏览器里执行。特点是一次性,不存储。

存储型(Stored)

恶意脚本被保存到了服务器(数据库、文件),之后每个访问该页面的用户都会中招。典型场景是评论区、留言板、个人简介。存储型危害最大,因为它影响所有访问者,且无需诱骗点击。

DOM 型(DOM-based)

漏洞完全发生在前端 JavaScript 中,服务器甚至不参与。前端代码直接把 URL 片段、用户输入写入 DOM(如用 innerHTML),没有经过安全处理,脚本就被执行。随着前端应用越来越重,DOM 型 XSS 越来越常见。

三、核心防御:输出编码

防御 XSS 的第一原则是 "在正确的上下文做正确的输出编码"

同一段用户输入,出现在不同位置需要不同的编码方式:

  • 放在 HTML 标签内容里 → HTML 实体编码(把 < > & 等转义)
  • 放在 HTML 属性里 → 属性编码
  • 放在 JavaScript 里 → JS 编码
  • 放在 URL 里 → URL 编码

核心思想是:让用户输入里的特殊字符失去语法意义,只作为纯文本显示。绝大多数现代模板引擎(以及 Vue、React 等框架)默认就会做 HTML 编码,这也是为什么用框架默认绑定通常是安全的。

危险信号:一旦你看到代码里用了 innerHTMLv-htmldangerouslySetInnerHTML,就要警惕 —— 这些是"绕过默认编码"的口子,必须配合消毒处理。

四、富文本场景:净化而非编码

有些场景(如富文本编辑器、Markdown 渲染)确实需要输出 HTML,不能简单编码。这时要用HTML 净化(Sanitization):

用成熟的净化库(如 DOMPurify、bluemonday)解析 HTML,按白名单只保留安全的标签和属性(如 <p><strong><a href>),剥离一切脚本、事件处理器(onclick)、javascript: 协议等危险内容。

原则是白名单而非黑名单:明确允许什么,而不是试图列举所有该禁止的。

五、纵深防御

Content Security Policy(CSP)

CSP 是浏览器层面的强力防线。通过 HTTP 响应头声明"只允许加载哪些来源的脚本",即使攻击者成功注入了脚本,只要不符合 CSP 策略,浏览器也会拒绝执行。一个严格的 CSP 能把 XSS 的危害大幅降低。

给敏感 Cookie(尤其是会话 ID)设置 HttpOnly 属性,JavaScript 就无法读取它。这样即使发生 XSS,攻击者也偷不走会话 Cookie,无法直接劫持登录态。

配合 SameSite 属性,进一步限制 Cookie 在跨站请求中的发送,缓解与 XSS 常常配合出现的 CSRF 攻击。

六、防御清单

  1. 默认输出编码:优先用框架的默认绑定,别手动拼 HTML。
  2. 慎用 innerHTML 类 API:非用不可时,务必先净化。
  3. 富文本走白名单净化:DOMPurify / bluemonday。
  4. 部署 CSP:浏览器层兜底。
  5. HttpOnly + SameSite:保护会话 Cookie。

七、小结

XSS 和 SQL 注入是"同一种病的两种表现" —— 都是数据被当成了代码。防御的思路也一脉相承:在数据进入解释器(这里是浏览器)之前,确保它只能是数据。 输出编码是地基,净化、CSP、HttpOnly 是层层加固。把这套体系建立起来,XSS 就很难有可乘之机。


本文为 KHack 社区原创教学内容,仅供安全研究与学习。

评论

还没有评论,来说两句。