Безопасно ли создание DOM Javascript / jQuery до тех пор, пока оно не будет добавлено в документ? - PullRequest
30 голосов
/ 08 апреля 2011

Пожалуйста, внимательно прочитайте это утверждение: давайте предположим, что перед добавлением ЛЮБЫХ элементов в document все небезопасные элементы в $ dom были удалены.Но они были изначально созданы.Хорошо, давайте продолжим ....


Если часть пользовательского текста обработана и может быть загружена следующим образом:

var comment = 'I\'m a naughty person!!' +
              '<script src="http://blah.com/some_naughty_javascript.js">';
var $dom = $('<div>' + comment + '</div>');

Это само по себе опасно в любом случае?Я хочу сказать, может ли простой процесс создания DOM каким-то образом внедрить что-либо, или он просто обрабатывается и создается структура?

Например:

var $dom = $('<script>alert("hi");</script>');

Очевидно, что сообщение hi не всплывает, пока не будет добавлено в document.Но:

  • Может ли какой-либо тег или что-либо созданное таким образом быть опасным?
  • Могут ли какие-либо функции в javascript / jquery "наблюдать" за элементами, создаваемыми таким образом, и действовать в соответствии с нимиДО того, как он был лишен плохих элементов и помещен в документ?

Bounty Edit

Так, как обрисовано в общих чертах в ответах ниже, кажется, что этот метод не очень безопасен,в частности, по одной причине:

  • var $dom = $('<img src="blah.jpg"/>') - это сразу запросит изображение, независимо от того, был ли объект добавлен в документ.

Это создаетглавная проблема для работы с HTML-запросами ajax.Например, если мы хотим получить значения из входных данных вида:

$.ajax({
  url: 'test.php',
  success: function(responseHTML) {
    var inputs = $(responseHTML).find('form input');
  }
});

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

Щедрость равнаприсуждается любому :

  • Кто может предоставить хороший, безопасный способ обработки запросов AJAX без вышеуказанной проблемы.
  • В идеале не предоставляет регулярного выражения.то есть, что если бы мы хотели сделать $(responseHTML).find('img') - удаление тегов изображений с помощью регулярных выражений не может быть вариантом, поэтому потребуется ненавязчивый способ остановить загрузку src, но при этом иметь те же атрибуты, структуру и т. д.

Ответы [ 4 ]

12 голосов
/ 08 апреля 2011

Это само по себе опасно?Я хочу сказать, может ли простой процесс создания DOM каким-то образом внедрить что-либо, или он просто обрабатывается и создается структура?

Простое создание элемента без добавления это для dom не приведет к запуску какого-либо скрипта, так как на данном этапе это просто объект (HtmlScriptElement).Когда он фактически добавлен в dom, элемент script будет оцениваться и запускаться браузером.С учетом сказанного я полагаю, возможно, что чрезвычайно хитрый человек может использовать ошибку, присутствующую в какой-либо платформе или браузере, которую вы можете использовать, чтобы вызвать нежелательный результат.

Учтите этопример:

<code><p>
    <input type="button" value="Store 'The Script' In Variable" id="store"/>
    <input type="button" value="Append 'The Script' To Dom" id="append"/>
</p>
<br/>
<p>
    <input type="button" value="Does nothing"/>
</p>
<h1>The Script</h1>
<pre id="script">
    $(function(){
        function clickIt(){
            $(this).clone().click(clickIt).appendTo("body");
        }
        $("input[type='button']").val("Now Does Something").click(clickIt);
    });
var theScript;$ ("# store"). click (function () {theScript = document.createElement ('script'); var scriptText = document.createTextNode ($ ("# script"). text ()); theScript.appendChild (scriptText);});$ ("# append"). click (function () {var head = document.getElementsByTagName ('head') [0]; head.appendChild (theScript);});

При нажатии на store он создаст HtmlScriptElement и сохранит его в переменной.Вы заметите, что ничего не запускается, даже если объект создан.Как только вы нажимаете append, скрипт добавляется в dom и сразу же оценивается, а кнопки делают что-то другое.

Пример кода на jsfiddle

Могут ли какие-либо функции в javascript / jquery "наблюдать" за элементами, создаваемыми таким образом, и действовать на них ДО того, как они будут удалены из плохих элементов и помещены в документ?

jQuery sort of делает это для вас уже, как это делает некоторый внутренний скрипт eval

С Карл Сведберг сообщение на .append()

Все методы вставки jQuery используют функцию domManip для очистки / обработки элементов до и после их вставкив ДОМ.Одна из функций, которую выполняет функция domManip, - это извлекать любые элементы сценария, которые собираются вставить, и запускать их через «подпрограмму evalScript», а не вставлять их с остальной частью фрагмента DOM.Он вставляет сценарии отдельно, оценивает их, а затем удаляет их из DOM....

Вы можете изменить поведение jQuery для удаления всех <script/> и очистки других элементов с помощью встроенного JavaScript onclick, mouseover, etc при вызове append(), однако это повлияет только на jQuery, поскольку кто-то может легко это сделатьиспользуйте vanilla javascript для добавления элемента <script/>.

События мутации Dom

Уровень Dom 2 определил некоторые события мутации Dom для захвата элементовкоторые добавляются в dom, можно посмотреть на событие, DOMNodeInserted .Однако он запускается после того, как элемент уже был добавлен. примечание , по данным Raynos в настоящее время устарело .

DOMNodeInserted Запускается, когда узел добавлен как дочерний узел другого узла.Это событие отправляется после того, как вставка произошла.Целью этого события является вставляемый узел.Пузыри: Да Отменяемые: Нет Контекстная информация: relatedNode содержит родительский узел

В конце концов, кажется, что нет полной остановки добавления <script/> к dom через некоторый другой javascript.(по крайней мере, не то, что я могу найти).

Лучший способ, который я могу предложить, - это никогда не доверять пользовательскому вводу, поскольку весь пользовательский ввод является злым .Когда вы делаете двойную проверку dom-манипуляций, чтобы убедиться, что нет запрещенных тегов, будь то <script/> или даже простые <p/> элементы, и очистите весь ввод перед его сохранением.

Также, как отмечает Джон, вам нужно беспокоиться о любом элементе , который может прикрепить событие onclick или любой встроенный обработчик события javascript.

7 голосов
/ 24 мая 2011

Обязательный ответ на ваш первый пример

var comment = 'I\'m a naughty person!!' +
              '<script src="http://blah.com/some_naughty_javascript.js">';
var $dom = $('<div>' + comment + '</div>');

Не делай этого. Вместо этого вы должны использовать API, который обрабатывает текст как текст и не подвергает вас инъекции. В этом примере вы должны сделать это:

var $dom = $('<div>').text(comment);

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

Переходя к вопросу

Если я вас правильно понимаю, вы хотели бы проанализировать информацию из произвольного HTML, без того, чтобы браузер не подготовился к ее отображению (скажем, путем загрузки изображений).

Это сложно , потому что DOM, встроенные в веб-браузеры, созданы для работы с контентом, который в какой-то момент будет отображаться. JQuery (и любая библиотека, которая создает узлы DOM) ограничен этим.

DOM Level 2 определяет API для создания документов , которые полностью отделены от активного: document.implementation.createHTMLDocument(<em>title</em>). В моем тестировании ничего не загружается при создании img для одного из следующих документов:

var doc = document.implementation.createHTMLDocument(''),
    img = doc.createElement('img');
img.src = 'http://example.com/image.jpg'; // Nothing happens.
// Alternatively…
doc.body.innerHTML = '<img src="http://example.com/image.jpg">'; // Nope.

Итак, документ, созданный таким образом, кажется хорошей песочницей для анализа и изучения HTML. Вы даже можете создать оболочку jQuery вокруг узлов в другом документе ($(doc.body)) и исследовать ее через API-интерфейсы jQuery. Найдя искомые узлы, вы можете преобразовать их обратно в HTML для вставки в активный документ или использовать методы , такие как importNode() и adoptNode(), чтобы перенести их непосредственно в активный документ .

К сожалению, поддержка всего этого является новой. Firefox поддерживает createHTMLDocument в версии 4 и выше (аналогичный метод createDocument, который работает с XML, доступен в более старых версиях ), а Internet Explorer поддерживает его в версии 9 и выше . Кроме того, насколько я могу судить, спецификация не гарантирует , что изображения и сценарии не будут предварительно загружены в эти документы.

Лучшим решением будет избежать парсера HTML браузера . Ряд JavaScript HTML парсеры появились в последнее время. Самым простым из них является, вероятно, Pure JavaScript HTML Parser Джона Ресига . Вы можете кормить его HTML, и он вызывает обратные вызовы при попадании в новые теги, атрибуты и текст. С помощью этих обратных вызовов вы можете создать новый HTML, построить DOM-узлы или сохранить документ в любой удобной для вас форме, а также можете игнорировать атрибуты и узлы, которые вы считаете опасными.

Пример этого можно найти в Interpolique Дэна Камински , проверке концепции, целью которой было уничтожить XSS и SQL-инъекцию раз и навсегда. Проект не сработал, но если вы скачаете Interpolique, вы обнаружите функцию safeParse(), застрявшую в нижней части htmlparser.js, которая использует белый список имен и атрибутов тегов и отбрасывает все остальное.

jsdom является полным (до DOM уровня 2, с некоторым уровнем 3) HTML DOM, написанным на JavaScript - вы можете использовать его для безопасной работы с HTML. Вы даже можете загрузить свою собственную копию jQuery. Однако он написан для CommonJS без учета совместимости с браузерами. Я не знаю, будет ли он работать в большинстве веб-браузеров без изменений. Это также большая библиотека.

Если это вообще возможно, идеальным решением является предоставление ответов AJAX в формате, отличном от HTML. Вам нужно вообще включать дополнительный небезопасный HTML? Если вы выполняете работу на сервере, возвращая только то, что вам нужно ...

{
    "inputs": [
        '<input …>',
        '<input …>'
    ],
}

... ваша работа на стороне клиента становится намного проще.

2 голосов
/ 08 апреля 2011

Отличный вопрос.Оказывается, можно внедрить скрипт и поместить в него обработчики событий.Я протестировал, используя следующий HTML:

<!DOCTYPE html>
<html lang="en">
    <head>  
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
        <script type="text/javascript"> 
            <!-- 
            $(function() {
                var $dom = $('<script>$(".testbutton").live("click", function() { alert("hi") });</script>');

                $(".firstbutton").click(function() {
                    $("BODY").append($dom);
                });
            });
            -->
        </script>
    </head>

    <body style="padding:0">            
        <button class="firstbutton">Click this first</button>

        <button class="testbutton">Then this</button>
    </body>
</html>

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

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

Это то, о чем я никогда не думал - спасибо, что поднял его.

0 голосов
/ 08 апреля 2011

Похоже, что скрипт не будет работать, если он не добавлен в DOM.

$(function ()
{
    var ss = document.createElement('script');
    var scr = 'alert("bah");';
    var tt = document.createTextNode(scr);
    ss.appendChild(tt);
    var hh = document.getElementsByTagName('head')[0];
    //hh.appendChild(ss);
});

И

$(function ()
{
    var ss = document.createElement('script');
    var scr = 'alert("bah");';
    var tt = document.createTextNode(scr);
    ss.appendChild(tt);
    var hh = document.getElementsByTagName('head')[0];
    hh.appendChild(ss);
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...