自libxml 2.9以来,在解析XML时已禁用加载外部实体,以防止XXE attacks。
在这种情况下,为了能够在使用PHP的DOMDocument解析XML时加载DTD文件,必须指定LIBXML_DTDLOAD
。
在启用LIBXML_DTDLOAD
之前,验证仅是否会加载预期的DTD的好方法是什么?
我能想到的一种方法(如下面的示例代码所示)是禁用实体加载,解析XML文件一次,检查DOCTYPE声明是否符合预期,然后在启用实体加载的情况下再次解析XML 。这就足够了吗?
<?php
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Publishing DTD v1.0 20120330//EN" "http://jats.nlm.nih.gov/publishing/1.0/JATS-journalpublishing1.dtd">
<article/>
XML;
// entity loading disabled
libxml_disable_entity_loader();
$doc = new DOMDocument;
$doc->loadXML($xml, LIBXML_DTDLOAD); // PHP Warning: DOMDocument::load(): I/O warning : failed to load external entity
print $doc->doctype->systemId; // http://jats.nlm.nih.gov/publishing/1.0/JATS-journalpublishing1.dtd
// entity loading enabled
libxml_disable_entity_loader(false);
$doc = new DOMDocument;
$doc->loadXML($xml, LIBXML_DTDLOAD);
print $doc->doctype->systemId; // http://jats.nlm.nih.gov/publishing/1.0/JATS-journalpublishing1.dtd
答案 0 :(得分:2)
在启用
LIBXML_DTDLOAD
之前,验证是否只会加载预期的DTD会有什么好方法?
如果要过滤(白名单)预期的DTD,可以通过从已设置为外部实体的可调用中返回NULL
来禁用所有其他DTD。装载机通过libxml_set_external_entity_loader
。
也就是说,如果DTD列入白名单,您将使用LIBXML_DTDLOAD
标志然后解析为函数中的resource handle。如果没有,则返回NULL
。
<?php
/**
* @link http://stackoverflow.com/q/24526493/367456
*/
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Publishing DTD v1.0 20120330//EN" "http://jats.nlm.nih.gov/publishing/1.0/JATS-journalpublishing1.dtd">
<article/>
XML;
/* own entity loader */
libxml_set_external_entity_loader(function() {
var_dump(func_get_args()); // just for demonstrating purposes
return NULL;
});
$doc = new DOMDocument;
$doc->loadXML($xml, LIBXML_DTDLOAD);
echo "----\n";
/* restore default entity loader */
libxml_set_external_entity_loader(NULL);
$doc = new DOMDocument;
$doc->loadXML($xml, LIBXML_DTDLOAD);
示例输出:
array(3) {
[0]=>
string(66) "-//NLM//DTD JATS (Z39.96) Journal Publishing DTD v1.0 20120330//EN"
[1]=>
string(66) "http://jats.nlm.nih.gov/publishing/1.0/JATS-journalpublishing1.dtd"
[2]=>
array(4) {
["directory"]=>
string(1) "/"
["intSubName"]=>
string(7) "article"
["extSubURI"]=>
string(66) "http://jats.nlm.nih.gov/publishing/1.0/JATS-journalpublishing1.dtd"
["extSubSystem"]=>
string(66) "-//NLM//DTD JATS (Z39.96) Journal Publishing DTD v1.0 20120330//EN"
}
}
Warning: DOMDocument::loadXML(): Failed to load external entity "-//NLM//DTD JATS (Z39.96) Journal Publishing DTD v1.0 20120330//EN" in Entity, line: 2 in /in/jemmH on line 18
----
Warning: DOMDocument::loadXML(): php_network_getaddresses: getaddrinfo failed: Name or service not known in /in/jemmH on line 25
Warning: DOMDocument::loadXML(http://jats.nlm.nih.gov/publishing/1.0/JATS-journalpublishing1.dtd): failed to open stream: php_network_getaddresses: getaddrinfo failed: Name or service not known in /in/jemmH on line 25
Notice: DOMDocument::loadXML(): failed to load external entity "http://jats.nlm.nih.gov/publishing/1.0/JATS-journalpublishing1.dtd" in Entity, line: 2 in /in/jemmH on line 25
答案 1 :(得分:-1)
您的方法似乎很好但是为了提高性能,您可能希望在DOCTYPE声明上执行白名单,如果它通过则您可以在启用实体加载的情况下解析它。