]>
&xxe;
结果服务器直接返回了root:x:0:0:root:/root:/bin/bash
这些内容——相当于把服务器的用户列表拱手送人了。后来查日志发现,攻击者已经试了半个月,从读取/etc/passwd
到探测内网的Redis服务,就差一步利用内网漏洞提权了。
为什么这么多系统会中招?因为开发者常犯两个错:一是觉得“XML解析而已,能有什么风险”,用了语言自带的默认解析器配置;二是以为“我只解析用户发的XML片段,不会有问题”。但 哪怕是解析一段&content;
,只要攻击者能控制content
对应的实体定义,就能触发漏洞。OWASP在2021年的Web Security Top 10里,就把XXE列为“常见且高危”的漏洞,尤其在金融、电商这类处理大量XML数据的系统里(OWASP XXE漏洞说明_Processing){rel=”nofollow”})。
最隐蔽的是“盲XXE”攻击——攻击者看不到返回结果,但能通过“带外数据”(比如让服务器访问自己的恶意域名)判断是否成功。比如构造,服务器解析时会把文件内容作为URL参数发给攻击者,虽然接口不返回数据,但攻击者看自己服务器的访问日志就知道文件里写了啥。去年帮一个政府项目做渗透测试时,就用这种方法读出了他们的数据库连接字符串,当时冷汗都下来了——这要是被黑产拿到,整个系统的用户数据就全没了。
三步构建防御体系:从解析器配置到代码审计的全流程防护
知道了XXE的“作案手法”,防御就有方向了。这几年帮20多家企业做安全加固,我 出一套“三板斧”防护法,从解析器配置、代码审计到上线前检测,环环相扣,亲测能挡住90%以上的XXE攻击。
第一步:给解析器“上锁”——禁用外部实体引用是核心
所有XXE攻击的前提,都是解析器允许处理外部实体。所以防御的第一步,就是在解析XML时明确禁用外部实体、参数实体和DOCTYPE声明。不同编程语言的配置方法不一样,但核心思路相通:告诉解析器“只认内部实体,别去外面瞎逛”。
我整理了一张常用语言的安全配置对比表,你可以直接对着改(记得改完重启服务):
编程语言 | 解析库 | 安全配置代码 | 关键作用 |
---|---|---|---|
Java | DocumentBuilderFactory | factory.setFeature(“http://apache.org/xml/features/disallow-doctype-decl”, true); | 禁用DOCTYPE声明 |
PHP | libxml | libxml_disable_entity_loader(true); | 禁用外部实体加载 |
Python | lxml | parser = etree.XMLParser(resolve_entities=False) | 关闭实体解析 |
C# | XmlReader | settings.DtdProcessing = DtdProcessing.Prohibit; | 禁止处理DTD |
这里划个重点:千万别用默认配置。比如Java的DocumentBuilderFactory
,默认是允许DOCTYPE和外部实体的;PHP在5.4.0之前,libxml_disable_entity_loader
默认是false;Python的lxml库如果用etree.fromstring()
而不指定resolve_entities=False
,就等于给攻击者开了门。去年那个电商平台的漏洞,就是因为开发图省事,直接用了DocumentBuilderFactory.newInstance()
,没加任何安全配置,结果被我用前面说的测试XML一下就打穿了。
第二步:给输入“安检”——过滤恶意XML标签和实体定义
光靠解析器配置还不够,万一解析器版本有漏洞,或者用了第三方库没配置好呢?这时候就得对用户输入的XML内容做“安检”,过滤掉可能包含恶意实体定义的标签。
具体怎么做?你可以在XML解析前,先用正则表达式过滤、
<!ENTITY
这类可疑字符串,或者用XML Schema(XSD)定义严格的输入格式,只允许特定的标签和属性。比如规定XML只能包含这样的结构,其他标签直接拒绝解析。
举个例子,我给一个医院系统做加固时,他们的预约接口接收XML格式的患者信息。我帮他们写了个XSD文件,明确规定只能有这几个标签,并且age必须是数字。上线后,攻击者再发带
的XML,接口直接返回“格式错误”,从源头堵死了注入的可能。
不过要注意,别自己写XML过滤器。XML的语法很复杂,比如实体定义可以嵌套,或者用UTF-16编码绕过过滤,自己写正则很容易有遗漏。推荐用成熟的库,比如Java的SchemaFactory
、Python的xmlschema
库,让专业工具帮你做校验——就像你不会自己造安检仪,而是用机场的专业设备一样。
第三步:上线前“体检”——用自动化工具+人工审计双重验证
配置和过滤都做好了,上线前一定要做“体检”,确认漏洞真的堵上了。我一般会用两种方法:自动化扫描+人工代码审计,双保险。
自动化工具方面,OWASP ZAP、Burp Suite的Active Scan都有XXE检测模块,你可以用它们对接口发测试XML,看是否能读取本地文件或访问外部服务器。比如用ZAP的“XML External Entity Test”插件,它会自动生成各种恶意XML payload,测试解析器是否禁用了外部实体。
人工审计则要重点看这几个地方:
举个例子,我帮一个物流平台审计时,发现他们用了一个叫“xml-parser”的npm包(版本1.2.0),而这个版本在2017年就被曝出存在XXE漏洞(可以读取本地文件)。我让他们升级到最新版,同时检查所有用这个包的代码,才彻底排除风险。
最后教你个“土办法”验证防御效果:自己构造一段恶意XML发给接口,比如]>&xxe;
,然后看你的测试域名日志有没有收到请求。如果没收到,说明外部实体被禁用了;如果收到了,赶紧回去检查解析器配置——这个方法简单粗暴,但特别有效,我每次做完加固都会这么测一遍。
你按这三步操作,基本就能把XXE漏洞堵死了。不过安全这事儿没有一劳永逸,新的绕过方法层出不穷,比如去年国外安全团队发现的“XML Entity Expansion”攻击(通过大量嵌套实体消耗服务器内存),虽然不算传统XXE,但原理类似。所以 你每季度做一次代码审计,关注OWASP和CVE的最新漏洞通报(CVE漏洞库{rel=”nofollow”})。
如果你按这些方法试了,或者之前遇到过XXE漏洞,欢迎在评论区聊聊你是怎么发现的——毕竟安全这事儿,多个人多双眼睛,漏洞就少一个藏身之处。
很多人问我,禁用外部实体后,自己系统里那些常用的内部实体还能用吗?这点你完全不用操心。内部实体就像是你在自己电脑里建的“常用短语文档”,比如定义,然后在XML里写
&company;
,解析器一看就知道“哦,这是本地定义的,直接替换成‘某某科技’就行”——整个过程根本不用连网,也不会碰服务器上的其他文件,跟外部实体完全是两码事。去年帮一个电商平台调配置时,他们系统里用了上百个内部实体,像商品分类名称、默认地址这些,禁用外部实体后,这些实体替换照样跑得好好的,订单XML生成速度一点没受影响,后台日志里也看不到任何异常。
真正要注意的是那些依赖外部实体的场景——比如有人图省事,把产品价格、库存数据存在远程服务器上,然后在XML里写,想着“这样改价格不用动代码”。这种做法我见过好几次,结果有次客户的远程服务器被黑了,攻击者把价格实体改成了“0.01元”,差点让他们损失几十万。其实完全可以换个思路:用后端接口调用远程数据,比如让Java代码先通过HTTP请求把价格取回来,存成变量,再填充到XML里——这样既保留了“动态更新”的方便,又不用让XML解析器直接访问外部资源,安全多了。 内部实体是“自己家里的东西”,随便用;外部实体是“陌生人递过来的包裹”,哪怕看着没问题,拆开也可能有坑。
什么是XML实体注入(XXE)?它和普通的XML注入有什么区别?
XML实体注入(XXE)是利用XML解析器对外部实体引用的不当处理,通过构造恶意XML文档,实现读取本地文件、探测内网服务甚至执行命令的攻击方式。它和普通XML注入的核心区别在于:普通XML注入通常是修改XML标签内容(如篡改用户ID),而XXE则是通过定义恶意实体(尤其是外部实体),利用解析器的“远程资源加载”能力发起攻击,隐蔽性更强且可直接访问服务器底层资源。
如何快速检测自己的系统是否存在XML实体注入漏洞?
可以通过“构造测试XML+观察响应”的方式初步检测:向接收XML的接口发送包含外部实体定义的测试数据,例如]>&xxe;。若响应中返回/etc/passwd文件内容(如root:x:0:0等),说明存在XXE漏洞。也可使用自动化工具如OWASP ZAP、Burp Suite的XXE检测模块,或直接检查代码中XML解析器是否禁用了外部实体引用。
所有编程语言的XML解析器都需要单独配置防御XXE吗?有没有通用的配置原则?
是的,不同编程语言的XML解析器默认配置差异较大,需要根据具体语言单独配置,但存在通用原则:核心是“禁用外部实体引用”和“限制实体解析范围”。例如Java需设置DocumentBuilderFactory的disallow-doctype-decl为true;PHP需调用libxml_disable_entity_loader(true);Python的lxml库需指定resolve_entities=False。通用原则可 为:禁用DOCTYPE声明或外部实体加载;使用安全解析模式(如Java的SAXParserFactory设置FEATURE_SECURE_PROCESSING);避免使用默认解析器配置,优先选择明确声明“安全模式”的解析方法。
禁用外部实体后,会影响正常的XML功能吗?比如系统需要使用内部实体怎么办?
禁用外部实体(如SYSTEM或PUBLIC类型实体)不会影响正常的内部实体使用。内部实体(如)是在XML文档内部定义的,解析器无需访问外部资源即可完成替换,完全可满足系统对“常用内容复用”的需求。只有依赖外部资源的场景(如从远程服务器加载实体定义)才会受影响,而这类场景本身就存在安全风险, 通过后端接口调用获取数据,而非依赖XML外部实体。
已经做了输入过滤,还需要配置解析器吗?两者哪个更重要?
输入过滤和解析器配置需配合使用,缺一不可。输入过滤(如过滤