XSS in Django verhindern
13. März 2023
0 Min. LesezeitCross-Site-Scripting (XSS) ist eine Art von Schwachstelle, bei der die Interaktion von Nutzern mit einer Webanwendung manipuliert wird, um die Browserumgebung eines Nutzers zu kompromittieren. Diese Schwachstellen können viele Web-Apps betreffen, darunter auch solche, die mit modernen Frameworks wie Django erstellt wurden.
Da XSS-Angriffe so weit verbreitet sind, müssen Sie Ihre Anwendungen unbedingt davor schützen. Dieser Leitfaden erläutert, wie XSS-Schwachstellen in Django-Apps entstehen und was Sie tun können, um sie zu entschärfen. Außerdem erfahren Sie, wie Sie kostenlose Sicherheitstools nutzen, um XSS-Schwachstellen frühzeitig in der Entwicklung zu erkennen und zu beheben.
Was ist XSS?
Der Begriff XSS stammt aus der Anfangszeit dieser Angriffe, als der Diebstahl websiteübergreifender Daten das Hauptziel von Angreifern war. XSS-Angriffe haben sich jedoch weiterentwickelt und gelten heute als alle Angriffe, die es Angreifern ermöglichen, clientseitige Daten, Tokens usw. zu kompromittieren. Erfolgreiche Angriffe können von der Übernahme einer Sitzung bis hin zur vollständigen Übernahme eines Kontos oder Systems führen.
Bei einem XSS-Angriff gelangen unerwünschte Daten in eine Anwendung und werden ohne Validierung an einen Nutzer zurückgegeben. Die als Teil der Antwort übermittelten schädlichen Daten bestehen häufig aus JavaScript oder anderem Code, der im Browser des Nutzers ausgeführt werden kann. Führt der Browser diesen Code aus, umgeht er seine Same-Origin-Policy (SOP) und der Nutzer wird zum Angriffsziel.
XSS-Angriffe wurden traditionell anhand der Persistenz der Daten in zwei Typen eingeteilt: reflektierte und gespeicherte XSS. Außerdem gibt es einen neueren Typ namens DOM-basiertes XSS.
Gespeichertes XSS
Angriffe mit gespeichertem XSS, manchmal auch persistentes XSS genannt, treten auf, wenn schädlicher Code auf dem Server einer anfälligen Anwendung gespeichert und später ohne Bereinigung an Nutzer gesendet wird (und schließlich vom Browser eines Nutzers ausgeführt wird). Die schädliche Payload wird häufig in Datenbanken, Forenbeiträgen, Protokollen oder Kommentarfeldern gespeichert.

Besucht ein Opfer die betroffene Anwendung, wird die schädliche Payload als Teil der Serverantwort im Browser des Opfers dargestellt. Der Browser führt diese Daten daraufhin aus, wodurch der Nutzer kompromittiert wird.
Reflektiertes XSS
Angriffe mit reflektiertem XSS treten auf, wenn vom Nutzer eingegebene und von der Anwendung zurückgegebene Daten nicht auf dem Server gespeichert werden. In diesem Fall wird der nicht vertrauenswürdige Code als Teil eines Suchergebnisses oder einer Fehlermeldung übermittelt, ohne ordnungsgemäß bereinigt zu werden.

Der Angreifer tarnt die Payload in der Regel, bevor er sie an das Opfer sendet. Klickt das Opfer auf die Payload, wird der schädliche Code an die betroffene Anwendung gesendet. Die Anwendung antwortet mit dem schädlichen Code, der im Browser des Opfers ausgeführt wird.
DOM-basiertes XSS
Angriffe mit DOM-basiertem XSS treten auf, wenn der Datenfluss der schädlichen Daten im Browser-DOM beginnt und endet. Die Schwachstelle wird ausgelöst, indem das DOM des Opfer-Browsers manipuliert wird. Die resultierende HTTP-Antwort ändert sich dabei nicht, sondern gibt lediglich den clientseitigen Code zurück, der anschließend das Browser-DOM so verändert, dass die Payload ausgeführt wird.
XSS in Django verhindern
Django bietet integrierte Schutzmaßnahmen gegen XSS-Angriffe, darunter einen Auto-Escape-Mechanismus für seine Template-Engine. Dieser kann zwar gängige XSS-Angriffe verhindern, jedoch keine Angriffsvektoren, die durch schlechte Programmierpraktiken entstehen.
Das Risiko von XSS-Angriffen lässt sich minimieren, indem Sie beim Entwickeln Ihrer App bewährte Vorgehensweisen einhalten.
Dynamische Daten in Anführungszeichen setzen
Jeder Teil Ihrer App, der Nutzereingaben ohne Bereinigung akzeptiert, kann XSS-Schwachstellen verursachen. Angreifer können solche Eingabefelder nutzen, um beliebigen JavaScript-Code einzuschleusen, für den keine HTML-Zeichen erforderlich sind.
Das folgende HTML verwendet beispielsweise ein Attribut ohne Anführungszeichen, das so verändert werden kann, dass es JavaScript-Handler enthält, etwa onmouseover=alert(1):
In der Praxis sind Angreifer natürlich nicht so harmlos. Stattdessen schleusen sie eigens erstellte Payloads ein, um diese Gelegenheit bestmöglich zu nutzen. Ein weiteres Beispiel für XSS-Schwachstellen durch Payloads ohne Anführungszeichen ist die Verwendung von JavaScript-Variablen zum Speichern von Nutzerdaten:
Wenn Sie JavaScript-Ganzzahlen auf diese Weise verwenden, um einen übermittelten Wert zu speichern, ist Ihre App XSS-Angriffen ausgesetzt. Djangos Auto-Escaping schützt davor nicht. Ein Angreifer kann etwas so Einfaches wie 23 ; payload() eingeben und so schädlichen Code im Browser eines Nutzers ausführen.
Setzen Sie alle nutzerseitig sichtbaren Attribute in doppelte Anführungszeichen, um diese Schwachstellen zu entschärfen:
Template-Literale vermeiden
Wenn Ihr Code JavaScript-Template-Literale enthält, ist er möglicherweise anfällig für XSS. Template-Literale verwenden Backticks statt Anführungszeichen und maskieren die Zeichen nicht:
Dadurch können Angreifer mit Payloads, die Backticks enthalten, erfolgreich Code ausführen. Am besten vermeiden Sie dies, indem Sie die Verwendung von Template-Literalen untersagen.
Attribut-URLs validieren
Viele HTML-Attribute wie a href und img src erwarten eine URL und können für XSS-Angriffe missbraucht werden. Angreifer können einfach Payloads mit einem javascript:-URI einfügen, um ihren Code auszuführen. Djangos HTML-Escaping schützt Sie in diesem Fall nicht.
Angenommen, Ihre App ermöglicht es Nutzern, die URL ihrer persönlichen Website hinzuzufügen. Böswillige Nutzer können als Link beispielsweise javascript:payload(XSS) verwenden. Klickt jemand auf diesen Link, wird die schädliche Payload ausgeführt.
Erstellen Sie eine Positivliste zulässiger Protokolle für URLs und sperren Sie alle anderen, um dieses Problem zu beheben. Zu den sicheren URIs gehören http:, https:, ftp: und mailto:. Sie können Attribut-URLs auch bereinigen, bevor Sie sie in der Datenbank speichern.
JavaScript-Daten maskieren
Wenn Sie variable Daten direkt in JavaScript einfügen, gelangen sie in den Ausführungskontext. Ermöglicht Ihr Programm Nutzern, Daten zu kontrollieren, die in JavaScript-Blöcke oder Event-Handler eingefügt werden, kann dies zu XSS-Angriffen führen.
Da Angreifer Code ohne HTML-Zeichen erstellen können, verhindert das Verlassen auf Djangos HTML-Escaping in solchen Fällen keine XSS-Angriffe.
Um das Risiko zu mindern, können Sie Template-Variablen in <script>-Blöcken untersagen oder zum Lesen von JS-Daten das Template-Tag json_script verwenden:
Denken Sie daran, Datenzeichen im Format \xHH zu kodieren und zu überprüfen, dass der Content-Type-Header für JSON den Wert application/json und nicht text/html hat.
CSS-Daten maskieren
In CSS-Style-Tags oder -Attributen eingefügte Daten können XSS-Schwachstellen in Ihrer Django-App verursachen. Angreifer können diese CSS-Daten nutzen, um in den Ausführungskontext zu gelangen. Dies geschieht in der Regel, indem JavaScript-Code in CSS-Kontexte eingeschleust wird:
Mit der Direktive expression() lassen sich beliebige JavaScript-Anweisungen zur Auswertung des Werts einer CSS-Eigenschaft verwenden. Dadurch wird Ihre Django-App anfällig für XSS-Angriffe.
Verwenden Sie zum Entschärfen dieses Risikos die Direktive expression() nicht, um Werte für CSS-Parameter festzulegen. Wenn Sie JavaScript-Anweisungen zum Festlegen oder Ändern von CSS-Eigenschaften verwenden, versuchen Sie es mit etwas wie style.property = x.
safe sparsam verwenden
Djangos Auto-Escaping ist in den meisten Fällen hilfreich, bietet aber keinen Schutz, wenn Sie das HTML-Escaping in einem Template mit dem Filter safe deaktivieren. Wenn Sie etwas als safe markieren, maskiert Django es nicht, sondern gibt die Daten unverändert aus.
Wenn Ihre App diese Abfrage ohne Datenvalidierung ausgibt, könnten Angreifer sie zum Einschleusen von Payloads nutzen. Verwenden Sie den safe-Filter nur, wenn Sie sicher sind, dass die Daten tatsächlich sicher sind. Von Nutzern bereitgestellte Daten dürfen Sie niemals als safe markieren.
Verwendung des safeseq-Filters einschränken
Der Filter safeseq gibt an, dass der auszugebende Inhalt vollkommen sicher ist. Genau wie safe kann er XSS-Payloads ermöglichen.
Vermeiden Sie diesen Filter für Sequenzdaten. Wenn Sie ihn dennoch verwenden müssen, greifen Sie auf mark_safe() zurück. So können Sie bei Code-Reviews gezielt alle Stellen prüfen, an denen Sie etwas ausdrücklich als sicher markiert haben.
html_safe() nur eingeschränkt verwenden
Die Methode html_safe() fügt der bereitgestellten Klasse die magische Methode __html__ hinzu, die eine exakte Zeichenfolgendarstellung dieser Klasse zurückgibt:
Django maskiert diese Daten nicht über seine Template-Engine. Sie stellen daher ein potenzielles XSS-Risiko dar. Verwenden Sie html_safe() nur, wenn es notwendig ist. Außerdem sollten Sie die magische Methode __html__ nicht in Klassen verwenden.
Filter mit is_safe=True vermeiden
Wenn Sie einen benutzerdefinierten Filter mit is_safe=True registrieren, maskiert Django das HTML nicht und markiert den vom Filter zurückgegebenen Wert als sicher.
Da die vom Filter zurückgegebenen Daten Djangos Auto-Escaping nicht durchlaufen, kann dies zu XSS-Angriffen führen. Registrieren Sie Filter auf diese Weise nur, wenn Sie sicher sind, dass die zurückgegebenen Daten bedenkenlos verwendet werden können.
mark_safe() und SafeString nur eingeschränkt verwenden
Die Methode mark_safe() kennzeichnet die ausgegebenen Daten als sicher und umgeht Djangos integrierten XSS-Schutz. Wenn Sie diese Methode häufig verwenden, kann das XSS-Schwachstellen begünstigen.
Außerdem gibt mark_safe() die Daten als SafeString zurück. Django verwendet die Klasse SafeString, um zu bestimmen, welche Daten sicher ausgegeben werden können und welche nicht. Was passiert also, wenn Sie SafeString direkt verwenden?
Wie Sie vielleicht vermutet haben, umgeht die direkte Verwendung der Klasse SafeString auf diese Weise Djangos HTML-Escaping und kann zu XSS führen. Verwenden Sie sowohl mark_safe() als auch SafeString möglichst sparsam.
Antworten nicht mit HttpResponse schreiben
Wenn Sie HttpResponse oder ähnliche Klassen direkt in Ihrem Code verwenden, umgehen Sie Djangos Template-System und deaktivieren dadurch das HTML-Escaping.
Dadurch kann Ihr Programm XSS-Angriffen ausgesetzt werden. Vermeiden Sie es daher, Antworten auf diese Weise zu schreiben. Verwenden Sie stattdessen die Methode render() mit einem Template.
Django-XSS-Schwachstellen mit Snyk erkennen und beheben
XSS-Angriffe zählen zu den aktivsten Bedrohungen für moderne Web-Apps. Da sie auf vielfältige Weise auftreten können, ist es schwierig, bei Code-Reviews alle XSS-Schwachstellen zu erkennen. Daher ist es entscheidend, diese Schwachstellen frühzeitig im Software Development Lifecycle (SDLC) aufzuspüren.
Integrieren Sie dazu spezielle Testtools in Ihre Entwicklungsumgebung. Wir veranschaulichen dies anhand von Snyk.
Snyk bietet mehrere Möglichkeiten, Django-Apps zu testen, darunter eine Web-UI, eine Befehlszeilenschnittstelle, IDE-Plug-ins und APIs. In diesem Tutorial verwenden Sie das Snyk-Plug-in für PyCharm. Folgen Sie der Anleitung mit einer installierten Version von PyCharm (Snyk unterstützt aber auch andere IDEs wie Eclipse und Visual Studio).
Um Snyk zu verwenden, erstellen Sie ein kostenloses Konto und installieren Sie anschließend das Plug-in gemäß der Installationsanleitung.

Nach der Installation des Plug-ins richten Sie das Snyk-Plug-in für Ihre IDE ein. Sie müssen das Plug-in mit der Snyk-Plattform verbinden. Dafür ist die Snyk CLI erforderlich, die Sie jedoch nicht separat installieren müssen. Beim ersten Scan lädt Snyk sie automatisch herunter.

Sie werden aufgefordert, sich bei Snyk zu authentifizieren. Klicken Sie auf Code jetzt testen, um zur Snyk-Web-UI zu gelangen.

Klicken Sie auf Authentifizieren, um die Integration Ihres Plug-ins zu bestätigen.

Nach der Authentifizierung analysiert Snyk Ihre Django-App automatisch und zeigt Probleme im Tab Snyk Ihrer IDE an. Alternativ können Sie die Analyse starten, indem Sie auf Scan ausführen klicken.

Snyk zeigt hilfreiche Informationen zu den erkannten Schwachstellen an, darunter deren Schweregrad und empfohlene Korrekturen.

Sie können die Schwachstellen beheben, indem Sie die von Snyk empfohlenen Upgrades übernehmen. Das Plug-in empfiehlt stets die geringstmöglichen Änderungen zur Korrektur Ihres Codes.
Fazit
XSS-Angriffe zählen zu den häufigsten Sicherheitsproblemen in Django-Anwendungen. Da sie viele Ursachen haben und in unterschiedlichen Formen auftreten können, lassen sie sich nur schwer vollständig verhindern. Mit den folgenden Best Practices können Sie das Risiko von XSS-Angriffen jedoch verringern.
Auch die richtigen Tools während der Entwicklung sind entscheidend. Eine dedizierte Security-Plattform wie Snyk hilft Ihnen, XSS-Schwachstellen frühzeitig in der Entwicklung Ihrer App zu finden und zu beheben.
Starten Sie mit Capture the Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.
