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.)