Очистить закодированный html текст (# десятичная нотация) из выходных данных AntiXSS v3

Привязываю для комментирования в движке блога XSS-safe. Пробовал много разных подходов, но очень сложно.

Когда я показываю комментарии, я сначала использую Microsoft AntiXss 3.0 для HTML-кодирования всего этого. Затем я пытаюсь HTML-декодировать безопасные теги, используя подход с использованием белого списка.

Читал пример Стива Даунинга в ветке Этвуда «Очистить HTML» на сайте refactormycode.

Моя проблема в том, что библиотека AntiXss кодирует значения в & # DECIMAL; обозначение, и я не знаю, как переписать пример Стива, так как мои знания регулярных выражений ограничены.

Я попробовал следующий код, в котором я просто заменил объекты на десятичную форму, но он не работает должным образом.

< with <
> with >

Моя перезапись:

class HtmlSanitizer
{
    /// <summary>
    /// A regex that matches things that look like a HTML tag after HtmlEncoding.  Splits the input so we can get discrete
    /// chunks that start with &lt; and ends with either end of line or &gt;
    /// </summary>
    private static Regex _tags = new Regex("&#60;(?!&#62;).+?(&#62;|$)", RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.Compiled);


    /// <summary>
    /// A regex that will match tags on the whitelist, so we can run them through 
    /// HttpUtility.HtmlDecode
    /// FIXME - Could be improved, since this might decode &gt; etc in the middle of
    /// an a/link tag (i.e. in the text in between the opening and closing tag)
    /// </summary>
    private static Regex _whitelist = new Regex(@"
^&#60;/?(a|b(lockquote)?|code|em|h(1|2|3)|i|li|ol|p(re)?|s(ub|up|trong|trike)?|ul)&#62;$
|^&#60;(b|h)r\s?/?&#62;$
|^&#60;a(?!&#62;).+?&#62;$
|^&#60;img(?!&#62;).+?/?&#62;$",


      RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace |
      RegexOptions.ExplicitCapture | RegexOptions.Compiled);

    /// <summary>
    /// HtmlDecode any potentially safe HTML tags from the provided HtmlEncoded HTML input using 
    /// a whitelist based approach, leaving the dangerous tags Encoded HTML tags
    /// </summary>
    public static string Sanitize(string html)
    {

        string tagname = "";
        Match tag;
        MatchCollection tags = _tags.Matches(html);
        string safeHtml = "";

        // iterate through all HTML tags in the input
        for (int i = tags.Count - 1; i > -1; i--)
        {
            tag = tags[i];
            tagname = tag.Value.ToLowerInvariant();

            if (_whitelist.IsMatch(tagname))
            {
                // If we find a tag on the whitelist, run it through 
                // HtmlDecode, and re-insert it into the text
                safeHtml = HttpUtility.HtmlDecode(tag.Value);
                html = html.Remove(tag.Index, tag.Length);
                html = html.Insert(tag.Index, safeHtml);
            }

        }

        return html;
    }

}

Мой HTML-код для тестирования ввода:

<p><script language="javascript">alert('XSS')</script><b>bold should work</b></p>

После AntiXss превращается в:

&#60;p&#62;&#60;script language&#61;&#34;javascript&#34;&#62;alert&#40;&#39;XSS&#39;&#41;&#60;&#47;script&#62;&#60;b&#62;bold should work&#60;&#47;b&#62;&#60;&#47;p&#62;

Когда я запускаю версию Sanitize (строка html) выше, она дает мне:

<p><script language="javascript">alert&#40;&#39;XSS&#39;&#41;</script><b>bold should work</b></p>

Регулярное выражение соответствует сценарию из белого списка, который мне не нужен. Любая помощь с этим будет принята с благодарностью.


person jesperlind    schedule 28.12.2008    source источник
comment
Только что вспомнил: codinghorror.com/blog/archives/001171.html   -  person some    schedule 28.12.2008
comment
Я просматривал все эти ссылки последние 24 часа. Не могу поверить, что это должно быть так сложно. Как они цитируют в комментариях к статье CSRF, веб-разработка страшна, по умолчанию это правда.   -  person jesperlind    schedule 28.12.2008
comment
Остерегайтесь занесенных в белый список тегов IMG. Атрибут onerror можно использовать для вставки скриптов.   -  person PEZ    schedule 28.12.2008
comment
Спасибо за внимание, Пез. Да, атрибуты изображения кажутся очень сложными для обработки, в любом случае не думаю, что они мне нужны в комментариях.   -  person jesperlind    schedule 28.12.2008
comment
img и a также могут иметь mousover, mouseout и другие события ...   -  person some    schedule 28.12.2008
comment
Да, на данный момент эта функция даже не останавливает простой ‹a href=javascript:alert('XSS')› XSS в href ‹/a›. Но у меня есть идея, что я могу использовать AntiXss.UrlEncode (строковый ввод), чтобы предотвратить это. Посмотрим, насколько этот подход будет работать.   -  person jesperlind    schedule 28.12.2008


Ответы (5)


Ваша проблема в том, что C # неправильно интерпретирует ваше регулярное выражение. Вам нужно избегать знака #. Без побега это слишком много совпадений.

private static Regex _whitelist = new Regex(@"
    ^&\#60;(&\#47;)? (a|b(lockquote)?|code|em|h(1|2|3)|i|li|ol|p(re)?|s(ub|up|trong|trike)?|ul)&\#62;$
    |^&\#60;(b|h)r\s?(&\#47;)?&\#62;$
    |^&\#60;a(?!&\#62;).+?&\#62;$
    |^&\#60;img(?!&\#62;).+?(&\#47;)?&\#62;$",

    RegexOptions.Singleline |
    RegexOptions.IgnorePatternWhitespace |
    RegexOptions.ExplicitCapture 
    RegexOptions.Compiled
 );

Обновление 2. Возможно, вас заинтересуют эти xss и regexp site.

person some    schedule 28.12.2008
comment
Спасибо. Я понял это, прочитав ссылки на регулярные выражения минуту назад. В вашей версии стартовые теги заменяются правильно. Но конечные теги не совпадают, так как косая черта кодируется в десятичной форме AntiXss. Но теперь мы рядом. - person jesperlind; 28.12.2008
comment
@jesperlind: О, я этого не искал. Обновил скрипт, поменял все / на (/) - person some; 28.12.2008
comment
Остерегайтесь атрибутов в тегах a и img ... Я предлагаю вам обрабатывать их специально и разрешать только href и src. - person some; 28.12.2008
comment
@some: Большое спасибо, теперь это отлично работает. Благодаря этому упражнению мои скудные знания RegEx стали намного лучше. - person jesperlind; 28.12.2008
comment
@jesperlind: Нет проблем, я выучил немного C #! Я добавил несколько ссылок, которые могут вас заинтересовать. - person some; 28.12.2008
comment
Спасибо за ссылки. Раньше я читал hack.ers.org, но мой RSS по какой-то причине некоторое время назад перестал работать. Подписка оформлена повторно. Очень интересен сайт тестирования JavaScript для RegEx. - person jesperlind; 28.12.2008

Рассматривали ли вы использование Markdown, VBCode или других подобных подходов, чтобы пользователи могли отмечать свои комментарии? Затем вы можете запретить весь HTML.

Если вы должны разрешить HTML, я бы подумал об использовании HTML-парсера (в духе HTMLTidy) и сделал бы там белый список.

person PEZ    schedule 28.12.2008

Да, я использую редактор WMD с уценкой, но я хочу, чтобы пользователи могли публиковать HTML и примеры кода, например, в Stack Overflow, поэтому я не хочу полностью запрещать HTML.

Я просматривал HTML Tidy, но еще не пробовал. Однако я использую Html Agility Pack, чтобы убедиться, что HTML правильный (без лишних тегов). Это делается перед запуском AntiXss.

Я попробую HTML Tidy, если я не смогу заставить мое текущее решение работать так, как мне нравится, спасибо за предложение.

person jesperlind    schedule 28.12.2008
comment
Но в Stackoverflow весь HTML экранирован. Нет списков вайтов. Или я ошибаюсь? - person PEZ; 28.12.2008
comment
Должен был опубликовать это как комментарий к вашему ответу ... Не уверен, как Stack Overflow справляется с этим, попробую. Работает ли ‹b› bold ‹/b› или мой аватар в качестве тега img: ‹img src = gravatar.com/avatar/ ›? - person jesperlind; 28.12.2008
comment
Думаю, вы правы, возможно, достаточно отключить html и принять только уценку. Интересно, используется ли в текстовом поле комментария в Stack Overflow редактор WDM, а также поле ответа? - person jesperlind; 28.12.2008
comment
Я проверил Html Agility Pack немного. На первый взгляд кажется, что это может помочь в добавлении в белый список. Но в любом случае лучше избегать всего HTML. - person PEZ; 28.12.2008

У меня Mac, поэтому я не могу протестировать ваш код C #. Но мне кажется, что вы должны сделать так, чтобы регулярное выражение _whitelist работало только с именами тегов. Это может означать, что вам нужно сделать два прохода, один для открытия и один для закрытия тегов. Но так будет намного проще.

person PEZ    schedule 28.12.2008
comment
Спасибо, что попробовали это, и за ваши полезные комментарии. Я тоже на Mac, но использую Visual Studio в разделе VMware Fusion Vista. - person jesperlind; 28.12.2008

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

Я также решил удалить тег img из белого списка, поскольку @Pez и @some отметили, что это может быть опасно разрешать.

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

class HtmlSanitizer
{
    /// <summary>
    /// A regex that matches things that look like a HTML tag after HtmlEncoding to &#DECIMAL; notation. Microsoft AntiXSS 3.0 can be used to preform this. Splits the input so we can get discrete
    /// chunks that start with &#60; and ends with either end of line or &#62;
    /// </summary>
    private static readonly Regex _tags = new Regex(@"&\#60;(?!&\#62;).+?(&\#62;|$)", RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.Compiled);


    /// <summary>
    /// A regex that will match tags on the whitelist, so we can run them through 
    /// HttpUtility.HtmlDecode
    /// FIXME - Could be improved, since this might decode &#60; etc in the middle of
    /// an a/link tag (i.e. in the text in between the opening and closing tag)
    /// </summary>

    private static readonly Regex _whitelist = new Regex(@"
^&\#60;(&\#47;)? (a|b(lockquote)?|code|em|h(1|2|3)|i|li|ol|p(re)?|s(ub|up|trong|trike)?|ul)&\#62;$
|^&\#60;(b|h)r\s?(&\#47;)?&\#62;$
|^&\#60;a(?!&\#62;).+?&\#62;$",


      RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace |
      RegexOptions.ExplicitCapture | RegexOptions.Compiled);

    /// <summary>
    /// HtmlDecode any potentially safe HTML tags from the provided HtmlEncoded HTML input using 
    /// a whitelist based approach, leaving the dangerous tags Encoded HTML tags
    /// </summary>
    public static string Sanitize(string html)
    {
        Match tag;
        MatchCollection tags = _tags.Matches(html);

        // iterate through all HTML tags in the input
        for (int i = tags.Count - 1; i > -1; i--)
        {
            tag = tags[i];
            string tagname = tag.Value.ToLowerInvariant();

            if (_whitelist.IsMatch(tagname))
            {
                // If we find a tag on the whitelist, run it through 
                // HtmlDecode, and re-insert it into the text
                string safeHtml = HttpUtility.HtmlDecode(tag.Value);
                html = html.Remove(tag.Index, tag.Length);
                html = html.Insert(tag.Index, safeHtml);
            }
        }
        return html;
    }
}
person jesperlind    schedule 28.12.2008
comment
Вам все равно нужно очистить содержимое a-тега, или можно использовать href = javascript: evil или onmouseover = evil и т. Д. - person some; 29.12.2008