javascript - Djangos escapejs-filter och XSS


Jag skulle vilja kunna templa vissa JavaScript-variabler med Djangos templatemotor. Situationen jag har är något liknande detta, där foo är en Python-sträng med användardefinierad data (läst från opålitlig data), som jag skulle vilja konvertera till en JavaScript-sträng.

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

Om jag läser Djangos dokumentation korrekt, är användning av escapejs på detta sätt sårbart för XSS-attacker.

Jag har tänkt på en potentiell lösning med HTML5:s data-* attribut på detta sätt.

<!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>

Jag undrar dock om det finns ett mindre omständligt/standardiserat sätt att göra detta.

Svar #1

Utdata från escapejs är säker att använda i det inbäddade sammanhanget av JS-inuti-HTML-text eller JS-inuti-HTML-citerat-attributvärde. Det gör det inte säkert att använda i JS-inuti-HTML-ociterat-attributvärde (men det gör inte heller escape för ociterade HTML-attribut, så citera alltid i vilket fall som helst).

Detta är OK eftersom det råkar undkomma alla HTML-specialtecken till JavaScript-strängliteral \u-undkomningar som inte innehåller HTML-specialtecken. Se _base_js_escapes i django.utils.html. Det markerar också utdata som ”säker”, så man kan hoppas att det inte är en lögn.

Huruvida det ska vara OK, och huruvida det är garanterat att förbli OK i framtida Django-versioner är oklart, med tanke på den dokumentation du pekade på. Det kan helt enkelt försöka säga att detta är fel form av escapning för vanlig HTML: även om det inte är sant att (med den nuvarande implementationen) escapningen är osäker för vanlig HTML, kommer du definitivt att få felaktig output med oönskade backslashes i den. Känner du dig lycklig?

Jag har tänkt på en potentiell lösning med HTML5:s data-* attribut på det här sättet.

Jag skulle alltid göra det på det här sättet i alla fall. Att använda mallar i JS är lätt att göra fel med och att ha inline-JS alls är lite rörigt och hindrar dig från att kunna använda Content-Security-Policy i framtiden.

Mycket bättre att lägga all din sidsdata i HTML-attribut, använda samma kända bra HTML-automatisk escapning genomgående, och låta JS hämta det från DOM. Du kan naturligtvis JSON-koda det när du vill inkludera strukturerad data istället för bara strängar.

(Jag skulle dock inte bry mig om en manuell osynlig div... du kan fästa attributet på <body> eller vilket annat element som helst som är kopplat till den aktuella datan.)