Какова максимальная глубина HTML-документов на практике?

#html #metrics #depth

#HTML #метрики #глубина

Вопрос:

Я хочу разрешить встраивание HTML, но избегать DoS из-за глубоко вложенных HTML-документов, которые приводят к сбою некоторых браузеров. Я хотел бы иметь возможность размещать 99,9% документов, но отклонять те, которые расположены слишком глубоко.

Два тесно связанных вопроса:

  1. Какие ограничения глубины документа встроены в браузеры? Например, browser X не выполняет синтаксический анализ или не создает документы с глубиной> некоторого предела.
  2. Доступна ли статистика глубины документов для документов в Интернете? Есть ли сайт с веб-статистикой, который объясняет, что некоторый процент реальных документов в Интернете имеет глубину документа меньше некоторого значения.

Глубина документа определяется как 1 максимальное количество родительских обходов, необходимых для достижения корня документа из любого узла в документе. Например, в

 <html>                   <!-- 1 -->
  <body>                 <!-- 2 -->
    <div>                <!-- 3 -->
      <table>            <!-- 4 -->
        <tbody>          <!-- 5 -->
          <tr>           <!-- 6 -->
            <td>         <!-- 7 -->
              Foo        <!-- 8 -->
  

максимальная глубина равна 8, поскольку текстовый узел «Foo» имеет 8 предков. Предок здесь интерпретируется не строго, т. Е. Каждый узел является его собственным предком и его собственным потомком.

Opera имеет некоторую статистику вложенности таблиц, которая предполагает, что 99,99% документов имеют глубину вложенности таблиц менее 22, но эти данные не содержат всей глубины документа.

Редактировать:

Если люди хотят критиковать библиотеку очистки HTML вместо того, чтобы отвечать на этот вопрос, пожалуйста, сделайте это. http://code.google.com/p/owasp-java-html-sanitizer/wiki/AttackReviewGroundRules объясняет, как найти код, где найти тестовый стенд, позволяющий опробовать атаки, и как сообщать о проблемах.

Редактировать:

Я спросил Адама Барта, и он очень любезно указал мне на код webkit, который обрабатывает это.

Webkit, по крайней мере, обеспечивает соблюдение этого ограничения. При создании treebuilder он получает настраиваемый предел дерева:

 m_treeBuilder(HTMLTreeBuilder::create(this, document, reportErrors, usePreHTML5ParserQuirks(document), maximumDOMTreeDepth**(document)))
  

и это проверяется с помощью теста block-nesting-cap.

Комментарии:

1. Мне любопытно, откуда вы взяли идею о том, что существует ограничение на вложенность или «глубоко вложенные HTML-документы, которые приводят к сбою некоторых браузеров»? Я никогда об этом не слышал.

2. Я думаю, что вложенность html на самом деле не является вашей самой насущной проблемой. Есть много злых вещей, которые пользователи могут делать с HTML. codinghorror.com/blog/2008/10 /…

3. @NickODell, я знаю, что есть много злых вещей, которые пользователи могут делать с HTML. На данный момент это самая насущная проблема, поскольку это единственная оставшаяся нерешенной проблема, выявленная в ходе первого раунда проверки атаки.

4. @WesleyMurch, если это проблема XY, какой вопрос я должен задать?

5. @NickODell, спасибо за ссылку. Эта реализация не подвержена проблемам, описанным в этом сообщении — она не использует регулярные выражения или какие-либо другие фильтры на основе шаблонов. Он маркирует HTML, применяет белые списки тегов и элементов, а затем использует нормализующий рендеринг для получения синтаксически корректного результата.

Ответ №1:

Возможно, стоит спросить coderesearch@google.com . Их изучение с 2005 года (http://code.google.com/webstats /) не охватывает ваш конкретный вопрос. Они отобрали более миллиарда документов, и им интересно услышать обо всем, что, по вашему мнению, стоит изучить.

—[Обновление]—

Вот грубый сценарий, который я написал для тестирования имеющихся у меня браузеров (ввод количества элементов для вложения в строку запроса):

 var n = Number(window.location.search.substring(1));

var outboundHtml = '';
var inboundHtml = '';

for(var i = 0; i < n; i  )
{
    outboundHtml  = '<div>'   (i   1);
    inboundHtml  = '</div>';
}

var testWindow = window.open();
testWindow.document.open();
testWindow.document.write(outboundHtml   inboundHtml);
testWindow.document.close();
  

И вот мои выводы (могут быть специфичны для моей машины, Win XP, 3 ГБ оперативной памяти):

  • Chrome 9: 3218 вложенных элементов будут отображаться, вкладка 3129 вылетает. (Chrome 9 старый, я знаю, программа обновления не работает в моей корпоративной локальной сети)
  • Safari 5: 3477 отобразит, браузер 3478 полностью закроется.
  • IE8: 1000000 будет отображаться (если позволяет память), хотя производительность значительно снижается при больших 4-значных числах из-за появления событий при прокрутке / перемещении мыши / и т. Д. Кажется, что все, что превышает 10000, блокируется, но я думаю, что это занимает очень много времени, поэтому эффективен DoS.
  • Opera 11: насколько я могу судить, просто ограничена памятью, т.Е. Моему скрипту не хватает памяти на 10000000. Для больших документов, которые все же отображаются, похоже, нет никакого снижения производительности, как в IE.
  • Firefox 3.6: ~ 1500000 будет отображаться, но тестирование выше этого диапазона привело к сбою браузера с помощью Mozilla Crash Reporter или просто зависанию, иногда число, которое сработало, в следующий раз не сработает, но большие числа ~ 1700000 приведут к сбою Firefox сразу после перезагрузки.

Подробнее о Chrome:

Изменение DIV на SPAN привело к тому, что Chrome смог вложить 9202 элемента до сбоя. Так что причина не в размере HTML (хотя элементы SPAN могут быть более легкими).

Вложенность 2077 ячеек таблицы ( <table><tr><td> ) работала (6231 элемент), пока вы не прокрутили вниз до ячейки 445, затем произошел сбой, поэтому вы не можете вложить 445 ячеек таблицы (1335 элементов).

Тестирование с файлами, сгенерированными из скрипта (в отличие от записи в новую Windows), дает несколько более высокие допуски, но Chrome все равно разбился.

Вы можете вложить 1409 элементов списка ( <ul><li> ) до его сбоя, что интересно, потому что:

  • Firefox перестает делать отступы в элементах списка после 99, возможно, из-за программного ограничения.
  • Opera продолжает делать отступы с сбоями при 250, 376, 502, 628, 754, 880…

Установка DOCTYPE эффективна в IE8 (перевод в стандартный режим, т. Е. var outboundHtml = '<!DOCTYPE html>'; ): Он не будет вкладывать 792 элемента списка (вкладка вылетает / закрывается) или 1593 DIVs. В IE8 не имело значения, был ли тест сгенерирован из скрипта или загружен из файла.

Таким образом, предел вложенности браузера, по-видимому, зависит от типа HTML-элементов, которые внедряет злоумышленник, и механизма компоновки. Может быть какой-то HTML значительно меньше этого. И у нас есть простой HTML DoS для пользователей IE8, Chrome и Safari со значительно небольшой полезной нагрузкой.

Кажется, если вы собираетесь разрешить пользователям публиковать HTML, который отображается на одной из ваших страниц, стоит рассмотреть ограничение на вложенные элементы, если существует ограничение на большой размер.

Комментарии:

1. Спасибо. Я не получил статистику, но я получил указатели на код webkit, который обеспечивает это. Я отредактировал OP с помощью указателей.

2. WRT. Firefox, я сам столкнулся с этой милой маленькой ошибкой: bugzilla.mozilla.org/show_bug.cgi?id=256180 В результате любые элементы глубиной более 200 просто не отображаются. Вы можете проверить это с помощью простого скрипта, который создает строку глубиной более 200 (я использовал 500 для аргументации), которая содержит известную строку, а затем проверяет, появляется ли известная строка где-либо при ее рендеринге.

Ответ №2:

Для webkit максимальная глубина документа настраивается, но по умолчанию она равна 512

http://trac.webkit.org/browser/trunk/Source/WebCore/page/Settings.h#L408

 static const unsigned defaultMaximumHTMLParserDOMTreeDepth = 512;
  

Комментарии:

1. Фантастика! Но происходит ли сбой браузера?

2. @LeeKowalkowski, WebCore не должен. Он сворачивает дочерние узлы, превышающие этот предел, в родительский, а не увеличивает стек, как в trac.webkit.org/browser/trunk/Source/WebCore/html/parser /… но другие браузеры действительно выходят из строя.

3. Мне удалось довольно легко вывести из строя Chrome, Safari и IE8, Firefox и Opera, похоже, просто не хватает памяти (не очевидно, является ли это моим сценарием или документом). Я включил свои выводы в свой ответ.