Проверить наличие вредоносного XML, прежде чем разрешить загрузку DTD?

Поскольку libxml 2.9, загрузка внешних объектов была отключена при разборе XML, чтобы предотвратить атаки XXE .

В этом случае, чтобы иметь возможность загружать DTD-файл при разборе XML с помощью DOMDocument PHP, необходимо указать LIBXML_DTDLOAD .

Что было бы хорошим способом проверить, будет ли загружен только ожидаемый DTD, до включения LIBXML_DTDLOAD ?

Один из подходов, который я могу придумать (как показано в приведенном ниже примере кода), заключается в том, чтобы отключить загрузку сущности, разобрать 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 

Что было бы хорошим способом проверить, будет ли загружен только ожидаемый DTD, до включения LIBXML_DTDLOAD ?

Если вы хотите отфильтровать (whitelist) ожидаемые DTD, вы можете сделать это, отключив все остальные, вернув NULL из вашего собственного вызываемого , который был установлен как внешний загрузчик libxml_set_external_entity_loader через libxml_set_external_entity_loader .

То есть вы должны использовать флаг LIBXML_DTDLOAD и затем разрешать дескриптор ресурса в своей функции, если 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 

Ваш подход кажется хорошим, но для улучшения работы вы можете захотеть сделать Белый список в объявлениях DOCTYPE, если он пройдет, тогда вы можете проанализировать его с включенной загрузкой объекта.