Как я могу использовать различные XML-библиотеки PHP для получения DOM-подобных функций и предотвращения уязвимостей DoS, таких как Billion Laughs или Quadratic Blowup?

Я пишу веб-приложение с XML API в PHP, и я беспокоюсь о трех конкретных уязвимостях, связанных с встроенными определениями DOCTYPE: локальное включение файлов, разложение квадратичной сущности и разложение экспоненциальной сущности. Мне бы хотелось использовать встроенные библиотеки PHP (5.3), но я хочу убедиться, что я не восприимчив к этим.

Я обнаружил, что я могу исключить LFI с помощью libxml_disable_entity_loader, но это не помогает с встроенными объявлениями ENTITY, включая объекты, которые относятся к другим объектам.

Библиотека SimpleXML (SimpleXMLElement, simplexml_load_string и т. Д.) Великолепна, потому что это парсер DOM, и все мои входы довольно малы; он позволяет мне легко использовать xpath и манипулировать DOM. Я не могу понять, как остановить объявления ENTITY. (Я был бы рад отключить все встроенные определения DOCTYPE, если это возможно.)

Библиотека XML Parser (xml_parser_create, xml_set_element_handler и т. Д.) Позволяет мне установить обработчик по умолчанию, который включает сущности, с xml_set_default_handler. Я могу взломать его для непризнанных объектов, он просто возвращает исходную строку (т. Е. «& Ent;»). Однако эта библиотека расстраивает: потому что это SAX-парсер, я должен написать кучу обработчиков (целых 9 …).

Так можно ли использовать встроенные библиотеки, получить объекты, подобные DOM, и защитить себя от этих различных уязвимостей DoS? благодаря

Эта страница описывает три уязвимости и предоставляет решение … если бы я использовал .NET: http://msdn.microsoft.com/en-us/magazine/ee335713.aspx

ОБНОВИТЬ:

<?php $s = <<EOF <?xml version="1.0?> <!DOCTYPE data [ <!ENTITY en "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...."> ]> <data>&en;&en;&en;&en;&en;&en;&en;&en;&en;&en;&en;&en;.....</data> EOF; $doc = new DOMDocument(); $doc->loadXML($s); var_dump($d->lastChild->nodeValue); ?> 

Я попробовал loadXML($s, LIBXML_NOENT); также. В обоих случаях я заканчиваю сброс 300+ МБ. Есть что-то, чего я все еще не хватает?

Примечание. Если вы создаете тестовые файлы с файлами, которые содержат фрагменты XML в следующем, ожидайте, что редакторы могут быть склонны к этим атакам, а также могут заморозить / сбой.

Миллиард смеется

 <?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> ]> <lolz>&lol9;</lolz> 

При загрузке:

FATAL: # 89: Обнаружен опорный контур сущности 1: 7
… (плюс шесть раз то же = семь раз общее с выше)
FATAL: # 89: обнаружен контур ссылки сущности 14:13

Результат:

 <?xml version="1.0"?> 

Использование памяти светлое, пик не затронут DOMDocument . Поскольку этот пример показывает 7 фатальных ошибок, можно сделать вывод, и действительно, это так, что эта загрузка без ошибок:

 <?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;"> ]> <lolz>&lol2;</lolz> 

Поскольку подстановка объекта не действует, и эта работа, попробуем

Квадратичная развязка

Вот и этот, сокращенный для вашего удовольствия от просмотра (мои варианты около 27 / 11kb):

 <?xml version="1.0"?> <!DOCTYPE kaboom [ <!ENTITY a "aaaaaaaaaaaaaaaaaa..."> ]> <kaboom>&a;&a;&a;&a;&a;&a;&a;&a;&a;...</kaboom> 

Если вы используете $doc->loadXML($src, LIBXML_NOENT); это работает как атака, в то время как я пишу это, скрипт по-прежнему загружается …. Так что на самом деле требуется некоторое время для загрузки и потребления памяти. Что-то вы можете играть сами. LIBXML_NOENT он работает безупречно и быстро.

Но есть оговорка, если вы, например, получаете nodeValue тега, вы получите расширения объектов, даже если вы не используете этот флаг загрузки.

Обходной путь для этой проблемы заключается в удалении узла DocumentType из документа. Обратите внимание на следующий код:

 $doc = new DOMDocument(); $doc->loadXML($s); // where $s is a Quadratic attack xml string above. // now remove the doctype node foreach ($doc->childNodes as $child) { if ($child->nodeType===XML_DOCUMENT_TYPE_NODE) { $doc->removeChild($child); break; } } // Now the following is true: assert($doc->doctype===NULL); assert($doc->lastChild->nodeValue==='...'); // Note that entities remain unexpanded in the output XML // This is not so good since this makes the XML invalid. // Better is a manual walk through all nodes looking for XML_ENTITY_NODE assert($doc->saveXML()==="<?xml version="1.0"?>\n<kaboom>&a;&a;&a;&a;&a;&a;&a;&a;&a;...</kaboom>\n"); // however, canonicalization will produce warnings because it must resolve entities assert($doc->C14N()===False); // Warning will be like: // PHP Warning: DOMNode::C14N(): Node XML_ENTITY_REF_NODE is invalid here 

Поэтому, хотя это обходное решение помешает XML-документу потреблять ресурсы в DoS, это упрощает создание недопустимого XML.

Некоторые цифры (я уменьшил размер файла, в противном случае это займет слишком много времени) ( код ):

 LIBXML_NOENT disabled LIBXML_NOENT enabled Mem: 356 184 (Peak: 435 464) Mem: 356 280 (Peak: 435 464) Loaded file quadratic-blowup-2.xml into string. Loaded file quadratic-blowup-2.xml into string. Mem: 368 400 (Peak: 435 464) Mem: 368 496 (Peak: 435 464) DOMDocument loaded XML 11 881 bytes in 0.001368 secs. DOMDocument loaded XML 11 881 bytes in 15.993627 secs. Mem: 369 088 (Peak: 435 464) Mem: 369 184 (Peak: 435 464) Removed load string. Removed load string. Mem: 357 112 (Peak: 435 464) Mem: 357 208 (Peak: 435 464) Got XML (saveXML()), length: 11 880 Got XML (saveXML()), length: 11 165 132 Got Text (nodeValue), length: 11 160 314; 11.060893 secs. Got Text (nodeValue), length: 11 160 314; 0.025360 secs. Mem: 11 517 776 (Peak: 11 532 016) Mem: 11 517 872 (Peak: 22 685 360) 

Я до сих пор не думал о стратегиях защиты, но теперь знаю, что загрузка миллиарда смеха в PHPStorm замерзнет, ​​например, и я прекратил тестирование позже, поскольку я не хотел его замораживать при написании этого.

Фактически вы должны проверить свое приложение с образцовыми документами и посмотреть, является ли он уязвимым.

Основной библиотекой для библиотек xml xml является libxml2. Это поведение контролируется из php в основном через необязательные константы, которые большинство библиотек принимают в качестве аргумента при загрузке xml.

Вы можете определить версию php libxml2 с помощью echo LIBXML_DOTTED_VERSION;

В более поздних версиях (после версии 2.6) libxml2 содержит ограничения замещения сущности, предназначенные для предотвращения как экспоненциальных, так и квадратичных атак. Они могут быть переопределены с LIBXML_PARSEHUGE опции LIBXML_PARSEHUGE .

По умолчанию libxml2 не загружает dtd, не добавляет атрибуты по умолчанию или не выполняет замещение сущности. Поэтому поведение по умолчанию – игнорировать dtds.

Вы можете включить части этого так:

  • LIBXML_DTDLOAD загрузит dtds.
  • LIBXML_NONET отключит сетевую загрузку dtds. Вы всегда должны иметь это и использовать каталог dtd libxml для загрузки dtds.
  • LIBXML_DTDVALID будет выполнять проверку LIBXML_DTDVALID во время разбора.
  • LIBXML_NOENT будет выполнять замещение сущности.
  • LIBXML_DTDATTR добавит атрибуты по умолчанию.

Поэтому, используя настройки по умолчанию, PHP / libxml2, вероятно, не уязвимы ни для одной из этих проблем, но единственный способ узнать наверняка – это проверить.