在允许DTD加载之前检查恶意XML?

时间:2014-07-02 08:35:19

标签: php xml security domdocument dtd

自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

2 个答案:

答案 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声明上执行白名单,如果它通过则您可以在启用实体加载的情况下解析它。