AntiXSS in ASP.Net Kernel

Microsoft Web Protection Library (AntiXSS) has reached the end of its life. The page says: "In .NET 4.0, the version of AntiXSS was included in the framework and can be enabled using the configuration. In ASP.NET v5, only the whitelisted encoder will be the only encoder."

I have a classic scripting scripting script: an ASP.Net Core solution in which users can edit text using the WYSIWYG HTML editor. The result is displayed for viewing by others. This means that if users inject JavaScript into the data that they send when they save text that this code can execute when others visit the page.

I want to have a whitelist of certain HTML codes (safe), but cut out the bad codes.

How can I do it? I cannot find any methods in ASP.Net Core RC2 to help me. Where is this whitelist encoder? How to call him? For example, I would need to clear the output returned via JSON WebAPI.

+11
source share
6 answers

There is a wiki in the main dot.net community.

You can enter encoders at the controller level (in the constructor) or as a reference System.Text.Encodings.Web .

More information can be seen here:

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

+4
source share

To perform an automatic Xss check, the old MVC used the logic implemented in the System.Web.CrossSiteScriptingValidation class. However, this class is missing from ASP.NET CORE 1. Therefore, to reuse it, I copied its code:

System.Web.CrossSiteScriptingValidation class

 // <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 the last char, it safe if (n == s.Length - 1) return false; matchIndex = n; switch (s[n]) { case '<': // If the < is followed by a letter or '!', it 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 unsafe (eg 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 to use the above class for all requests, I created Middleware that use the 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<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; } } 
+3
source share

You can use System.Text.Encodings.Web for software coding in .NET Standard. It offers HTML, JavaScript, and URL encoders. This should be equivalent to AntiXss, because the use of a whitelist is documented :

By default, encoders use a safe list limited to the base Latin Unicode range and encode all characters outside this range as equivalent character codes.

+3
source share

It looks like you need some kind of white list sanitizer. OWASP AntiSamy.NET did this, but I no longer think it is supported. If the data is always delivered in JSON, you can also run through DOMPurify on the client side before adding it to the DOM. The presence of malicious HTML in JSON itself is not so dangerous (at least not until you have set the content type and X-content-type: nosniff correctly). The code will not run until it is displayed in the DOM.

+2
source share

This is a good question. I want to point out one thing: we should never try to create our own disinfectant. They are very difficult to understand correctly. It is much better to use a library created and maintained by an authoritative author.

From OWASP : "OWASP recommends using a security-oriented coding library to ensure that these rules are correctly implemented."

If you are using the .NET Framework, this library may still work: https://docs.microsoft.com/en-us/dotnet/api/system.web.security.antixss.antixssencoder?view=netframework-4.8

For .NET Core, the System.Text.Encodings library can also help, as described above. https://docs.microsoft.com/en-us/aspnet/core/security/cross-site-scripting?view=aspnetcore-2.2#accessing-encoders-in-code

+1
source share

If you really want to secure the input, that is, only allow a certain set of HTML elements, simply coding the content will not help much. You need an HTML disinfectant.

Creating such a thing is not an easy task. You will need some method for HTML analysis and a set of rules about what to allow and what not. So that in the future, new HTML tags do not cause security problems, I recommend using the white list.

There are at least two open HTML sanitation libraries that run on .NET Core, one of which I wrote several years ago. Both are available as NuGet packages:

They use various HTML disassemblies as the back end. You may need to tweak the rule sets a bit to match what your WYSIWYG editor creates.

0
source share

All Articles