Search code examples
javascriptjquerydomxss

How can I prevent DOM XSS when loading AJAX response into .html()


I use an AJAX .get() method to load HTML for a "Product Search" app and render it into a HTML node ($searchModalBody):

   productSearch: function productSearch(e, element) {
        var $searchModal = $('#product-search-modal');
        var $searchModalBody = $searchModal.find('.modal-body');
        var url = $searchModal.data('url');
        ...
        $.get(url, {
            fromQuote: true, customerNumber: custNumber, hasQtyField: false
        }).done(function (response) {   
            $searchModalBody.html(response);
        })

How can I sanitize the response to avoid any DOM XSS?

I've read that .text() is preferred over .html(), but since I am loading HTML, .text() will just escape all of it.

Sorry if I have missed anything. I've been trying to read up on XSS prevention, but couldn't find anything. I might just not fully understand what I need to be doing/looking for.


Solution

  • You should avoid using .html() and .innerHTML as it introduces security risks as you mentioned.

    jquery documentation:

    Do not use these methods to insert strings obtained from untrusted sources such as URL query parameters, cookies, or form inputs. Doing so can introduce cross-site-scripting (XSS) vulnerabilities. Remove or escape any user input before adding content to the document.

    MDN on innerHTML:

    Note: This is a security risk if the string to be inserted might contain potentially malicious content. When inserting user-supplied data you should always consider using Element.SetHTML() instead, in order to sanitize the content before it is inserted.

    So, using setHTML (experimental) might be a little bit safer, demo:

    const elm = document.querySelector('.demo');
    
    elm.setHTML('<script>alert("XSS");</script\><h1>Oups!</h1>');
    <div class="demo"></div>


    Also, OWASP recommends this library:

    HTML Sanitization will strip dangerous HTML from a variable and return a safe string of HTML. OWASP recommends DOMPurify for HTML Sanitization.

    let clean = DOMPurify.sanitize(dirty);
    

    There are some further things to consider:

    • If you sanitize content and then modify it afterwards, you can easily void your security efforts.
    • If you sanitize content and then send it to a library for use, check that it doesn’t mutate that string somehow. Otherwise, again, your security efforts are void. You must regularly patch DOMPurify or other HTML Sanitization libraries that you use.
    • Browsers change functionality and bypasses are being discovered regularly.

    Note that Sanitizer object that setHTML uses is still experimental

    const sanitizer = new Sanitizer();
    
    const theRawHTML = '<script>alert("XSS");</script\><h1>Oups!</h1>';
    
    $('.demo').html(sanitizer.sanitize(theRawHTML));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    
    <div class="demo"></div>


    TLDR;

    • Avoid rendering HTML from untrusted sources, and find alternatives such as sending your inputs as JSON which you build the HTML for.
    • Rely on trusted and well-maintained libraries such as DOMPurify to sanitize your HTML