【问题标题】:Check for malicious XML before allowing DTD loading?在允许 DTD 加载之前检查恶意 XML?
【发布时间】:2014-08-22 23:18:42
【问题描述】:

从 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

【问题讨论】:

  • 满足什么?您能否将此作为一个具体的编程问题? -- 可能重复:Clarifications on XXE vulnerabilities throughout PHP versions(待回答)
  • 如前所述,问题是“在启用 LIBXML_DTDLOAD 之前,验证 加载预期 DTD 的好方法是什么?”
  • (这意味着):如果您使用LIBXML_DTDLOAD,这些 DTD 是否实际加载?你测试过吗?另请在您的问题中提供示例数据和代码。我们需要一个例子来重现和清晰 - 至少如果你想要一个充分而清晰的答案。根据我在链接问题中的测试,无论您的设置如何,这些都不会加载。但我不确定该测试的稳定性。
  • 我已将示例代码添加到问题中。
  • +1 为活泼的例子 :)

标签: php xml security domdocument dtd


【解决方案1】:

您的方法似乎不错,但为了提高性能,您可能希望对 DOCTYPE 声明进行白名单,如果通过,则可以在启用实体加载的情况下对其进行解析。

【讨论】:

    【解决方案2】:

    在启用LIBXML_DTDLOAD 之前,验证只有预期的DTD 将被加载的好方法是什么?

    如果您想过滤(白名单)预期的 DTD,您可以通过从您自己的 callable 设置为 外部实体加载器 来自libxml_set_external_entity_loader

    也就是说,您将使用LIBXML_DTDLOAD 标志,然后在您的函数中解析为resource handle,以防DTD 被列入白名单。如果没有,你回复说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
    

    【讨论】:

    • 谢谢,这似乎是个好方法。你知道XML解析完成后如何恢复默认的外部实体加载器吗?
    • 没有必要正常恢复外部实体加载器,因为您已经处理了加载,因为您已经为列入白名单的 DTD 模仿了默认行为。但是,如果您真的想恢复默认处理程序,请致电libxml_set_external_entity_loader(NULL);。一个例子在这里:3v4l.org/jemmH
    猜你喜欢
    • 1970-01-01
    • 2015-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-24
    • 2019-05-25
    • 1970-01-01
    相关资源
    最近更新 更多