AntiXSS in ASP.Net Core

后端 未结 6 1781
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-10 12:17

Microsoft Web Protection Library (AntiXSS) has reached End of Life. The page states \"In .NET 4.0 a version of AntiXSS was included in the framework and could be enabled via

6条回答
  •  有刺的猬
    2020-12-10 12:41

    To execute automatic Xss check, the old MVC used the logic implemented in the System.Web.CrossSiteScriptingValidation class. However this class is not present in ASP.NET CORE 1. So, to reuse it I copied its code:

    System.Web.CrossSiteScriptingValidation class

    // 
    //     Copyright (c) Microsoft Corporation.  All rights reserved.
    // 
    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
    }
    

    Than,in order to use the above class for all requests, I created a Middleware that use CrossSiteScriptingValidation class:

    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 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;
        }
    }
    

提交回复
热议问题