AntiXSS в ядре ASP.Net

Библиотека веб-защиты Microsoft (AntiXSS) достиг конца жизни. На странице указано: «В .NET 4.0 версия AntiXSS была включена в платформу и могла быть включена через конфигурацию. В ASP.NET v5 кодировщик на основе белого списка будет единственным кодировщиком».

У меня есть классический сценарий межсайтового скриптинга: решение ASP.Net Core, в котором пользователи могут редактировать текст с помощью WYSIWYG html-редактора. Результат отображается для других, чтобы увидеть. Это означает, что если пользователи вводят JavaScript в данные, которые они отправляют при сохранении текста, этот код может выполняться, когда другие посещают страницу.

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

Как мне это сделать? Я не могу найти какие-либо методы в ASP.Net Core RC2, чтобы помочь мне. Где этот кодировщик белого списка? Как мне это вызвать? Например, мне нужно очистить вывод, возвращаемый через JSON WebAPI.

Ответы на вопрос(4)

Ты можешь использоватьSystem.Text.Encodings.Web для программного кодирования в .NET Standard. Он предлагает кодировщики HTML, JavaScript и URL. Это должно быть эквивалентно AntiXss, потому что этодокументированный использовать белый список:

По умолчанию кодировщики используют безопасный список, ограниченный диапазоном базового латинского Unicode, и кодируют все символы за пределами этого диапазона в качестве эквивалентов кодов символов.

Похоже, вам нужно какое-то дезинфицирующее средство на основе белого списка. OWASP AntiSamy.NET использовал для этого, но я не думаю, что это больше поддерживается. Если данные всегда доставляются в JSON, вы также можете запустить их через DOMPurify на стороне клиента, прежде чем добавлять их в DOM. Наличие вредоносного HTML в самом JSON не так уж и опасно (по крайней мере, до тех пор, пока вы правильно установите параметры типа содержимого и параметры X-содержимого-типа: заголовки nosniff). Код не сработает, пока он не будет отображен в DOM.

 Tedd Hansen23 июн. 2016 г., 11:17
За исключением нескольких электронных писем (где я могу обмануть и удалить все <[^>] +>), я могу запустить все через что-то вроде DOMPurify. Однако не запускайте все через JSON, иногда это также записывается непосредственно на страницу. Было бы много работы по замене каждого @ Html.Raw (Model.Foobar) на <script> document.write (DOMPurify (UnBase64 (@ Html.Raw (Base64 (Model.Foobar)))))) </ скрипт>. (Необходимо кодировать данные base64, поскольку любые теги <script> </ script> внутри будут выполняться парсером HTML, а HtmlEncode отключит теги, которые вы хотите сохранить. + Некоторые проблемы с utf8.)

Выполнить автоматическийXss проверьте, что старый MVC использовал логику, реализованную вSystem.Web.CrossSiteScriptingValidation учебный класс. Однако этот класс отсутствует в ASP.NET CORE 1. Итак, чтобы использовать его повторно, я скопировал его код:

Класс System.Web.CrossSiteScriptingValidation
// <copyright file="CrossSiteScriptingValidation.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
public static class CrossSiteScriptingValidation
{
    private static readonly char[] StartingChars = { '<', '&' };

    #region Public methods

    // Only accepts http: and https: protocols, and protocolless urls.
    // Used by web parts to validate import and editor input on Url properties. 
    // Review: is there a way to escape colon that will still be recognized by IE?
    // %3a does not work with IE.
    public static bool IsDangerousUrl(string s)
    {
        if (string.IsNullOrEmpty(s))
        {
            return false;
        }

        // Trim the string inside this method, since a Url starting with whitespace
        // is not necessarily dangerous.  This saves the caller from having to pre-trim 
        // the argument as well.
        s = s.Trim();

        var len = s.Length;

        if ((len > 4) &&
            ((s[0] == 'h') || (s[0] == 'H')) &&
            ((s[1] == 't') || (s[1] == 'T')) &&
            ((s[2] == 't') || (s[2] == 'T')) &&
            ((s[3] == 'p') || (s[3] == 'P')))
        {
            if ((s[4] == ':') || ((len > 5) && ((s[4] == 's') || (s[4] == 'S')) && (s[5] == ':')))
            {
                return false;
            }
        }

        var colonPosition = s.IndexOf(':');
        return colonPosition != -1;
    }

    public static bool IsValidJavascriptId(string id)
    {
        return (string.IsNullOrEmpty(id) || System.CodeDom.Compiler.CodeGenerator.IsValidLanguageIndependentIdentifier(id));
    }

    public static bool IsDangerousString(string s, out int matchIndex)
    {
        //bool inComment = false;
        matchIndex = 0;

        for (var i = 0; ;)
        {

            // Look for the start of one of our patterns 
            var n = s.IndexOfAny(StartingChars, i);

            // If not found, the string is safe
            if (n < 0) return false;

            // If it's the last char, it's safe 
            if (n == s.Length - 1) return false;

            matchIndex = n;

            switch (s[n])
            {
                case '<':
                    // If the < is followed by a letter or '!', it's unsafe (looks like a tag or HTML comment)
                    if (IsAtoZ(s[n + 1]) || s[n + 1] == '!' || s[n + 1] == '/' || s[n + 1] == '?') return true;
                    break;
                case '&':
                    // If the & is followed by a #, it's unsafe (e.g. S) 
                    if (s[n + 1] == '#') return true;
                    break;

            }

            // Continue searching
            i = n + 1;
        }
    }

    #endregion

    #region Private methods

    private static bool IsAtoZ(char c)
    {
        return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
    }

    #endregion
}

Затем, чтобы использовать вышеупомянутый класс для всех запросов, я создал Middleware, который используетCrossSiteScriptingValidation учебный класс:

AntiXssMiddleware
public class AntiXssMiddleware
{
    private readonly RequestDelegate _next;
    private readonly AntiXssMiddlewareOptions _options;

    public AntiXssMiddleware(RequestDelegate next, AntiXssMiddlewareOptions options)
    {
        if (next == null)
        {
            throw new ArgumentNullException(nameof(next));
        }

        _next = next;
        _options = options;
    }       

    public async Task Invoke(HttpContext context)
    {
        // Check XSS in URL
        if (!string.IsNullOrWhiteSpace(context.Request.Path.Value))
        {
            var url = context.Request.Path.Value;

            int matchIndex;
            if (CrossSiteScriptingValidation.IsDangerousString(url, out matchIndex))
            {
                if (_options.ThrowExceptionIfRequestContainsCrossSiteScripting)
                {
                    throw new CrossSiteScriptingException(_options.ErrorMessage);
                }

                context.Response.Clear();
                await context.Response.WriteAsync(_options.ErrorMessage);
                return;
            }
        }

        // Check XSS in query string
        if (!string.IsNullOrWhiteSpace(context.Request.QueryString.Value))
        {
            var queryString = WebUtility.UrlDecode(context.Request.QueryString.Value);

            int matchIndex;
            if (CrossSiteScriptingValidation.IsDangerousString(queryString, out matchIndex))
            {
                if (_options.ThrowExceptionIfRequestContainsCrossSiteScripting)
                {
                    throw new CrossSiteScriptingException(_options.ErrorMessage);
                }

                context.Response.Clear();
                await context.Response.WriteAsync(_options.ErrorMessage);
                return;
            }
        }

        // Check XSS in request content
        var originalBody = context.Request.Body;
        try
        {                
            var content = await ReadRequestBody(context);

            int matchIndex;
            if (CrossSiteScriptingValidation.IsDangerousString(content, out matchIndex))
            {
                if (_options.ThrowExceptionIfRequestContainsCrossSiteScripting)
                {
                    throw new CrossSiteScriptingException(_options.ErrorMessage);
                }

                context.Response.Clear();
                await context.Response.WriteAsync(_options.ErrorMessage);
                return;
            }

            await _next(context);
        }
        finally
        {
            context.Request.Body = originalBody;
        }            
    }

    private static async Task<string> ReadRequestBody(HttpContext context)
    {
        var buffer = new MemoryStream();
        await context.Request.Body.CopyToAsync(buffer);
        context.Request.Body = buffer;
        buffer.Position = 0;

        var encoding = Encoding.UTF8;
        var contentType = context.Request.GetTypedHeaders().ContentType;
        if (contentType?.Charset != null) encoding = Encoding.GetEncoding(contentType.Charset);

        var requestContent = await new StreamReader(buffer, encoding).ReadToEndAsync();
        context.Request.Body.Position = 0;

        return requestContent;
    }
}
 Basil Kosovan08 июл. 2019 г., 12:28
Ты спас мой день.

У основного сообщества dot.net есть вики на это.

Вы можете вводить кодировщики на уровне контроллера (в конструкторе) или ссылатьсяSystem.Text.Encodings.Web.

Больше информации можно увидеть здесь:

https://docs.microsoft.com/en-us/aspnet/core/security/cross-site-scripting

Ваш ответ на вопрос