Question

I'd like to be able to template some JavaScript variables using Django's template language. The situation I have is something like this, where foo is a Python string of user defined data (read untrusted data), which I'd like to convert to a JavaScript string.

<!doctype html>
<html>
    <head>
        <script>
            bar = '{{ foo|escapejs }}';        
        </script>
    </head>
    <body>
    </body>
</html>

If I'm reading Django's documentation correctly, using escapejs like this is vulnerable to XSS attacks.

I've thought of a potential solution using HTML5's data-* attribute like this.

<!doctype html>
<html>
    <head>
        <script>
            window.onload = function () {
                bar = document.getElementById('data').getAttribute('data-bar');
            };
        </script>
    </head>
    <body>
        <div id="data" style="display:none;" data-bar="{{ foo }}"></div>
    </body>
</html>

However, I'm wondering if there's a less cumbersome/standard approach to this.

Was it helpful?

Solution

The output of escapejs is safe for use in the nested context of JS-inside-HTML-text or JS-inside-HTML-quoted-attribute-value. It doesn't make safe for use in JS-inside-HTML-unquoted-attribute-value (but then neither does escape for unquoted HTML attributes, so always quote in any case).

This is OK because it happens to escape all the HTML-special characters to JavaScript string literal \u escapes that do not contain HTML-special characters. See _base_js_escapes in django.utils.html. It also marks the output as ‘safe’, so you would hope that is not a lie.

Whether it is supposed to be OK, and whether it is guaranteed to stay OK in future Django versions is unclear, given the documentation you pointed out. It may be just trying to say that this is the wrong form of escaping for plain HTML: whilst it isn't true that (with the current implementation) the escaping is unsafe for plain HTML, you will certainly get wrong output with unwanted backslashes in it. Do you feel lucky?

I've thought of a potential solution using HTML5's data-* attribute like this.

I would always do it this way in any case. Templating into JS is easy to get wrong and having the inline JS at all is a bit messy and prevents you from deploying Content-Security-Policy in the future.

Much better to put all your page data in HTML attributes, using the same known-good HTML autoescaping throughout, and have the JS retrieve it from the DOM. You can of course JSON-encode it when you want to include structured data rather than just strings.

(I wouldn't bother with a manual invisible div though... you can attach the attribute to <body> or whatever other element is connected to the data in question.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top