Client-side HTML Sanitizer module to prevent XSS and unwanted tags in UGC.
This is the module version of the great work from jitbit
- Very fast (8000 ops/sec)
- Very small (1.7kb unminified!)
- Zero dependency, vanilla JS, works even in IE (duh)
Please note: to prevent XSS attacks you should always sanitize input on the server too. Never trust the client!
npm install jitbit-html-sanitizer
(simply puts the script into /node_modules
)
import HtmlSanitizer from 'jitbit-html-sanitizer';
let html;
const sanitizer = new HtmlSanitizer({
allowedTags: ['div', 'p', 'span', 'table', 'tr', 'td', 'a', 'form'],
allowedAttributes: ['href', 'style', 'src', 'class', 'id'],
allowedCss: ['font-weight', 'height', 'width'],
allowedSchemas: ['http:', 'https:', 'ws:']
});
//run with default settings
html = sanitizer.sanitizeHtml("<div><script>alert('xss!');</sc" + "ript></div>"); //returns "<div></div>";
html = sanitizer.sanitizeHtml("<a onclick=\"alert('xss')\"></a>"); //returns "<a></a>";
html = sanitizer.sanitizeHtml("<a href=\"javascript:alert('xss')\"></a>"); //returns "<a></a>";
//form is allowed in initialization for all future invocations
html = sanitizer.sanitizeHtml("<form></form>"); //returns "<form></form>";
//allow something only once by specifying a selector
html = sanitizer.sanitizeHtml("<input type=checkbox>", "input[type=checkbox]"); //returns "<input type=\"checkbox\">";
The sanitizer uses whitelisting approach (as opposed to "blacklisting") to clean out everything that's not allowed.
It uses browser/DOM to parse the html by using DOMParser
object (hence the browser "front-end only" requirement) which
makes it much faster than "pure JavaScript" sanitizers.
Tested on https://www.bbc.co.uk
homepage - the page is sanitized ~370 times per second on an i5 core CPU in
Firefox Quantum (tested via benchmark.js
)
Comparing HtmlSanitizer vs DOMPurify benchmark:
starting benchmark...
HtmlSanitizer x 8,048 ops/sec ±3.37% (44 runs sampled)
DOMPurify x 5,195 ops/sec ±3.30% (57 runs sampled)
Fastest is HtmlSanitizer
a, abbr, b, blockquote, body, br, center, code, dd, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, label, li, ol, p, pre, small, source, span, strong, sub, sup, table, tbody, tr, td, th, thead, ul, u, video
align, color, controls, height, href, id, src, style, target, title, type, width
background-color, color, font-size, font-weight, text-align, text-decoration, width
http:, https:, data:, m-files:, file:, ftp:, mailto:, pw:
(allowed in 'src', 'href' and similar "uri-attributes". To clean up stuff like <a href='javascript:alert()'></a>
)
Allowed tags, attributes and styles are listed in class properties and can be overridden on initialization:
const sanitizer = new HtmlSanitizer({
allowedTags: ['div', 'p', 'span', 'table', 'tr', 'td', 'a', 'form'],
allowedAttributes: ['href', 'style', 'src', 'class', 'id'],
allowedCss: ['font-weight', 'height', 'width'],
allowedSchemas: ['http:', 'https:', 'ws:']
});
To allow an extra tag only once during invocation - specify extra selector to allow in the second parameter
var html = sanitizer.sanitizeHtml("<input type=checkbox>", "input[type=checkbox]");
Supported by all major browsers, IE10 and higher.
Why create a front-end HTML sanitizer if the input has to be sanitized on the server anyway?
Users often copy-paste awful HTML generated by MS Word, MS Outlook or Apple Mail that needs a clean-up. Or you need to remove excessive formatting in an WYSIWYG editor. Or you need to display an (ugly) email message in a (beatuful) mobile app. Or (my favorite) you simply need to ease the load in the server-side sanitizer. And many many other use-cases.
© Jitbit