现代xml解析器默认禁用system外部实体,需显式配置才能启用,且各库开关方式不同;路径解析基准为当前工作目录而非xml所在目录;启用后存在xxe风险,应优先禁用doctype声明或改用显式api加载。

XML解析器默认禁用SYSTEM外部实体,不配置就根本不会加载
绝大多数现代XML解析器(如Python的xml.etree.ElementTree、Java的DocumentBuilder、libxml2)默认关闭外部实体解析,哪怕XML里写了,也不会读取。这不是“写法不对”,而是安全策略生效了——你得主动打开,且不同库开关位置和方式完全不同。
-
lxml需显式传resolve_entities=True并禁用load_dtd=False(否则报错) -
xml.etree.ElementTree根本不支持SYSTEM实体,连开关都没有,硬上会直接忽略或抛ParseError - Java的
DocumentBuilderFactory要调setFeature("http://apache.org/xml/features/disallow-doctype-decl", false)再设setFeature("http://xml.org/sax/features/external-general-entities", true)
用SYSTEM引用本地文件时路径行为极不统一
相对路径基准点不是XML文件所在目录,而是解析器当前工作目录(pwd),这点在脚本中极易出错。比如SYSTEM "config.xml"可能去读/home/user/config.xml而非/opt/app/config.xml,且Windows和Linux对file://协议处理差异大。
- 绝对路径如
SYSTEM "/etc/hosts"在Linux下有效,但Windows需写成SYSTEM "C:\Windows\System32\drivers\etc\hosts"或SYSTEM "file:///C:/Windows/System32/drivers/etc/hosts" - HTTP路径如
SYSTEM "http://example.com/entity.txt"依赖解析器是否启用网络访问(很多生产环境默认禁用) - 路径中含空格或中文时,
file://必须URL编码,否则lxml直接报IOError: Error reading file
开启SYSTEM实体=打开XXE漏洞入口,没做隔离等于裸奔
一旦允许SYSTEM,攻击者可通过恶意DTD触发任意文件读取、SSRF甚至命令执行(如PHP的expect://)。不是“我只读自己文件就安全”,因为实体可嵌套、可间接引用、可由用户输入拼接。
- 禁用
DOCTYPE声明是最彻底方案:lxml设resolve_entities=False且no_network=True;ElementTree干脆不用它 - 若真需外部内容,改用显式API加载:
with open("config.xml") as f: tree = ET.parse(f),而非依赖解析器自动拉取 - Java中务必同时关闭
external-parameter-entities和load-external-dtd,单关一个仍可能被绕过
SYSTEM实体无法跨解析上下文复用,别指望一次定义到处用
实体作用域严格限定在声明它的XML文档内,SYSTEM引用的文件内容只是文本替换,不会变成全局变量或导入模块。常见误解是以为定义了后,所有后续XML都能用&version;——实际每个文件都得单独声明。
- 实体内容不解析嵌套:如果
version.txt里含<tag></tag>,会被原样插入,不会被当XML节点处理 - 字符编码必须与主文档一致,否则
lxml报UnicodeDecodeError,不能靠encoding="utf-8"参数覆盖外部文件编码 - 修改外部文件后,已解析的DOM树不会自动更新,重载需重新调用
parse()










