Как проверить, безопасна ли строка HTML?

В моем приложении мне нужно отправить и получить HTML в виде строки. Я хотел бы сохранить безопасность, и поэтому мне нужно проверить, соответствуют ли элементы dom в строке допустимым тегам, а также допустимы ли объявления стилей и нет ли внедренных сценариев. Первое, что приходит на ум, это, конечно, регулярное выражение строки, но это утомительно, может быть с ошибками и наверняка неэффективно. Вторая идея - использовать что-то под названием XPath, но хотя я читал некоторые материалы на сайте MDN, я все еще не знаю, как реализовать этот пример кода:

const XPathResult           = Components.interfaces.nsIDOMXPathResult;

const ALLOWED_TAGS          = ['div', 'span', 'b', 'i', 'u', 'br', 'font', 'img'];
const ALLOWED_STYLES        = ['font-weight', 'font-size', 'font-family', 'text-decoration', 'color', 'background-color'];
const ALLOWED_ATTRIBUTES    = ['style', 'name'];

const XPATH_PART_TAGS = ALLOWED_TAGS.map(function (v) {
    return "name() != '" + v + "' and name() != '" + v.toUpperCase() + "'"; // case insensitive
}).join(' and ');

const XPATH_PART_ATTRS = ALLOWED_ATTRIBUTES.map(function (v) {
    return "name() != '" + v + "' and name() != '" + v.toUpperCase() + "'"; // case insensitive
}).join(' and ');


const XPATH_BAD_TAGS        = "//*[(namespace-uri() != 'http://www.w3.org/1999/xhtml') or (" + XPATH_PART_TAGS + ")]";
const XPATH_BAD_ATTRIBUTES  = "//@*[((namespace-uri() != 'http://www.w3.org/1999/xhtml') and (namespace-uri() != '')) or (" + XPATH_PART_ATTRS+ ")]";
const XPATH_STYLE           = "//@*[name() = 'style']";


/**
 * Checks if inline style definition is considered secure
 *
 * @param {String} styleValue value of style attribute
 * @return bool
 */
function isStyleSecure(styleValue) {
    var styles = styleValue.split(';'),
        style,
        name, value,
        i, l;
    for (i = 0, l = styles.length; i < l; i++) {
        style = styles[i].trim();
        if (style === '') {
            continue;
        }
        style = style.split(':', 2);
        if (style.length !== 2) {
            return false;
        }
        name = style[0].trim().toLowerCase();
        value = style[1].trim();

        if (ALLOWED_STYLES.indexOf(name) === -1) {
            return false;
        }
    }
    return true;
}

/**
 * Singleton that verifies if given XHTML document fragment is considered secure.
 * Uses whitelist-based checks on tag names, attribute names and document namespaces.
 *
 * @class
 * @namespace core.SecurityFilter.MessageSecurityFilter
 */
var MessageSecurityFilter = {
    /**
     * Checks if given document fragment is safe
     *
     * @param {nsIDOMElement} element root element of the XHTML document fragment to analyze
     * @return {bool} true if fragment is safe, false otherwise
     */
    isSecure: function SecurityFilter_isSecure(element) {
        var document = element.ownerDocument,
            result,
            attr;

        result = document.evaluate('//*', element, null, XPathResult.ANY_TYPE, null);

        result = document.evaluate(XPATH_BAD_TAGS, element, null, XPathResult.ANY_TYPE, null);
        if (result.iterateNext()) {
            return false;
        }
        result = document.evaluate(XPATH_BAD_ATTRIBUTES, element, null, XPathResult.ANY_TYPE, null);
        if ((attr = result.iterateNext())) {
            return false;
        }

        result = document.evaluate(XPATH_STYLE, element, null, XPathResult.ANY_TYPE, null);
        while ((attr = result.iterateNext())) {
            if (!isStyleSecure(attr.nodeValue)) {
                return false;
            }
        }

        return true;
    }

};

И первая идея состояла в том, чтобы создать documentFragment, а затем проверить его узлы с помощью treeWalker или просто следовать dom-дереву с помощью.firstChild и т. Д. Но я думаю, что это решение небезопасно, так как оно оставит меня открытым для всех внедряемых сценариев. Я прав?

Есть ли другой путь?

2 ответа

Решение

Не катите свое собственное дезинфицирующее средство. Используйте тот, который был написан кем-то, кто знает темные уродливые углы HTML, CSS и JS.

См. http://code.google.com/p/google-caja/wiki/JsHtmlSanitizer для получения дезинфицирующего средства JavaScript.

Уровень безопасности, который вам нужен, зависит от того, как вы обрабатываете HTML. Если вы отправляете его по электронной почте или отображаете на веб-сервере, вам нужно быть намного более осторожным, чем если бы вы просто анализировали текст на нем.

Предполагая, что вы отображаете это на веб-сервере, это очень сложная проблема, и вам следует использовать очиститель HTML, такой как http://htmlpurifier.org/ и подписаться на обновления безопасности, возможно, даже найти способ автоматически получать обновления, Для дополнительной безопасности также используйте iframe. Также обратите особое внимание, если вы каким-то образом избегаете HTML.

Конечно, правильный ответ может быть совершенно другим в зависимости от фактического контекста вашей проблемы. Вышесказанное должно обработать наиболее распространенный случай.

Также смотрите, как RegEx соответствует открытым тегам, кроме автономных тегов XHTML

Другие вопросы по тегам