Какова максимальная глубина HTML-документов на практике?
Я хочу разрешить встраивание HTML, но избегать DoS из-за глубоко вложенных HTML-документов, которые разбивают некоторые браузеры. Я хотел бы иметь возможность разместить 99,9% документов, но отклонить те, которые гнездятся слишком глубоко.
два тесно связанных вопроса:
- какие ограничения глубины документа встроены в браузеры? Е. Г. браузер X не разобрать или не создавать документы с глубиной > какой-то предел.
- статистика глубины документа для документов, доступных на веб? Есть ли сайт с веб-статистикой, который объясняет, что некоторый процент реальных документов в интернете имеет глубину документа меньше некоторого значения.
глубина документа определяется как 1 + максимальное количество родительских обходов, необходимых для достижения корня документа из любого узла в документе. Например, в
<html> <!-- 1 -->
<body> <!-- 2 -->
<div> <!-- 3 -->
<table> <!-- 4 -->
<tbody> <!-- 5 -->
<tr> <!-- 6 -->
<td> <!-- 7 -->
Foo <!-- 8 -->
максимальная глубина равна 8, так как текстовый узел " Foo " имеет 8 предков. Предок здесь интерпретируется не строго, т. е. узел, своя предок и его собственный потомок.
Опера имеет некоторую статистику вложенности таблиц, которая предполагает, что 99,99% документов имеют глубину вложенности таблиц менее 22, но эти данные не содержат всю глубину документа.
EDIT:
Если люди хотели бы критиковать библиотеку дезинфекции HTML Вместо ответа на этот вопрос, пожалуйста, сделайте. http://code.google.com/p/owasp-java-html-sanitizer/wiki/AttackReviewGroundRules объясняет как найти код, где найти тестовый стенд, который позволяет опробовать атаки, и как сообщать о проблемах.
EDIT:
Я спросил Адама Барта, и он очень любезно указал мне на код webkit, который обрабатывает это.
Webkit, по крайней мере, применяет это ограничение. Когда treebuilder is создано он получает ограничение дерева, которое настраивается:
m_treeBuilder(HTMLTreeBuilder::create(this, document, reportErrors, usePreHTML5ParserQuirks(document), maximumDOMTreeDepth**(document)))
и оно испытано блок-вложенность-cap
2 ответов
возможно, стоит спросить 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, 3GB Ram):
- Chrome 9: 3218 вложенные элементы будут отображаться, вкладка 3129 сбоев. (Chrome 9 старый, я знаю, ошибка обновления в моей корпоративной локальной сети)
- Safari 5: 3477 отобразит, браузер 3478 полностью закроется.
- IE8: 1000000+ будет рендеринг (память позволяет), хотя производительность значительно снижается, когда в высокие 4-значные номера из-за события пузырится при прокрутке / перемещении мышь / etc. Ничего более 10000 появляется запереть, но я думаю просто занимает очень много времени, поэтому эффективен DoS.
- Opera 11: просто ограничен памятью, насколько я могу судить, т. е. у моего скрипта заканчивается память для 10000000. Для больших документов, которые делают рендеринг, хотя, похоже, нет никакого снижения производительности, как в IE.
- Firefox 3.6: ~1500000 будет отображаться, но тестирование выше этого диапазона привело к сбою браузера с Mozilla Crash Репортер или просто висит, иногда номер, который работал, потерпел бы неудачу в последующее время, но большие числа ~1700000 разрушили бы Firefox прямо с перезапуска.
подробнее о Chrome:
изменение DIV на промежуток привело к тому, что Chrome смог вложить 9202 элемента перед сбоем. Таким образом, причина не в размере HTML (хотя элементы SPAN могут быть более легкими).
вложенность 2077 ячеек таблицы (<table><tr><td>
) работал (6231 elements), пока вы не прокрутили до ячейки 445, затем она разбилась, поэтому вы не можете вложить 445 ячеек таблицы (1335 элементов).
тестирование с файлами, сгенерированными из скрипта (в отличие от записи в новые окна), дает немного более высокие допуски, но 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, который отображается на одной из ваших страниц, стоит рассмотреть ограничение на вложенные элементы, если есть щедрый предел размера.
для webkit максимальная глубина документа настраивается, но по умолчанию это 512
http://trac.webkit.org/browser/trunk/Source/WebCore/page/Settings.h#L408
static const unsigned defaultMaximumHTMLParserDOMTreeDepth = 512;