Sicherheit von Django-Webanwendungen
Der Schutz von Benutzerdaten ist ein wesentlicher Bestandteil der Gestaltung jeder Website. Einige der häufigsten Sicherheitsbedrohungen haben wir bereits im Artikel Websicherheit erläutert. Dieser Artikel zeigt anhand praktischer Beispiele, wie die integrierten Schutzmechanismen von Django mit solchen Bedrohungen umgehen.
| Voraussetzungen: | Lesen Sie das Thema „Website-Sicherheit“ zur serverseitigen Programmierung. Bearbeiten Sie die Themen des Django-Tutorials mindestens bis einschließlich Django-Tutorial Teil 9: Arbeiten mit Formularen. |
|---|---|
| Ziel: | Die wichtigsten Maßnahmen kennenzulernen, die Sie ergreifen oder vermeiden sollten, um Ihre Django-Webanwendung abzusichern. |
Überblick
Das Thema Website-Sicherheit gibt einen Überblick darüber, was Website-Sicherheit bei der serverseitigen Entwicklung bedeutet und gegen welche häufigen Bedrohungen Sie sich schützen sollten. Eine der wichtigsten Aussagen des Artikels lautet: Fast alle Angriffe sind erfolgreich, wenn die Webanwendung Daten aus dem Browser vertraut.
Warnung:
Die wichtigste Lehre zur Website-Sicherheit lautet: Vertrauen Sie niemals Daten aus dem Browser. Dazu gehören GET-Anfragedaten in URL-Parametern, POST-Daten, HTTP-Header und Cookies, von Benutzern hochgeladene Dateien usw. Prüfen und bereinigen Sie alle eingehenden Daten. Gehen Sie immer vom Schlimmsten aus.
Die gute Nachricht für Django-Benutzer ist, dass das Framework viele der häufigsten Bedrohungen abwehrt! Der Artikel Sicherheit in Django (Django-Dokumentation) erläutert die Sicherheitsfunktionen von Django und wie Sie eine Django-gestützte Website absichern.
Häufige Bedrohungen und Schutzmechanismen
Statt hier die Django-Dokumentation zu wiederholen, demonstrieren wir in diesem Artikel einige Sicherheitsfunktionen anhand unseres Django-Tutorials zu LocalLibrary.
Cross-Site-Scripting (XSS)
XSS bezeichnet eine Klasse von Angriffen, bei denen Angreifer clientseitige Skripte über eine Website in die Browser anderer Benutzer einschleusen. Dies geschieht meist, indem schädliche Skripte in der Datenbank gespeichert werden, wo sie später abgerufen und anderen Benutzern angezeigt werden. Eine andere Möglichkeit besteht darin, Benutzer zum Anklicken eines Links zu bewegen, der das JavaScript des Angreifers in deren Browser ausführt.
Das Template-System von Django schützt Sie vor den meisten XSS-Angriffen, indem es bestimmte in HTML „gefährliche“ Zeichen maskiert. Wir können dies demonstrieren, indem wir versuchen, über das Formular zum Anlegen von Autoren, das wir in Django-Tutorial Teil 9: Arbeiten mit Formularen eingerichtet haben, JavaScript in unsere LocalLibrary-Website einzuschleusen.
-
Starten Sie die Website mit dem Entwicklungsserver (
python3 manage.py runserver). -
Öffnen Sie die Website in Ihrem lokalen Browser und melden Sie sich mit Ihrem Superuser-Konto an.
-
Rufen Sie die Seite zum Anlegen eines Autors auf (sie sollte unter
http://127.0.0.1:8000/catalog/author/create/erreichbar sein). -
Geben Sie Namen und Datumsangaben für einen neuen Benutzer ein und hängen Sie im Feld „Last Name“ den folgenden Text an:
<script>alert('Test alert');</script>.
Hinweis: Dieses Skript ist harmlos. Wenn es ausgeführt wird, zeigt es in Ihrem Browser ein Hinweisfenster an. Erscheint der Hinweis beim Absenden des Datensatzes, ist die Website für XSS-Angriffe anfällig.
-
Klicken Sie auf Submit, um den Datensatz zu speichern.
-
Nach dem Speichern wird der Autor wie unten dargestellt angezeigt. Dank der XSS-Schutzmechanismen sollte
alert()nicht ausgeführt werden. Stattdessen erscheint das Skript als reiner Text.
Wenn Sie den HTML-Quellcode der Seite anzeigen, sehen Sie, dass die gefährlichen Zeichen der Script-Tags durch ihre harmlosen Escape-Sequenzen ersetzt wurden (beispielsweise ist > nun >).
<h1>
Author: Boon<script>alert('Test alert');</script>, David
(Boonie)
</h1>
Django-Templates schützen Sie vor den meisten XSS-Angriffen. Allerdings lässt sich dieser Schutz deaktivieren. Außerdem wird er nicht automatisch auf alle Tags angewendet, deren Werte normalerweise nicht aus Benutzereingaben stammen (beispielsweise wird der help_text eines Formularfelds gewöhnlich nicht von Benutzern bereitgestellt, weshalb Django diese Werte nicht maskiert).
XSS-Angriffe können auch von anderen nicht vertrauenswürdigen Datenquellen ausgehen, etwa von Cookies, Webdiensten oder hochgeladenen Dateien – immer dann, wenn die Daten vor dem Einfügen in eine Seite nicht ausreichend bereinigt werden. Wenn Sie Daten aus solchen Quellen anzeigen, müssen Sie möglicherweise eigenen Code zur Bereinigung hinzufügen.
Content Security Policy (CSP)
Eine Content Security Policy (CSP) bietet eine zusätzliche Schutzebene gegen XSS und andere Injection-Angriffe. Eine CSP ist ein HTTP-Header, der dem Browser mitteilt, aus welchen Quellen eine Seite Inhalte wie Skripte, Stylesheets und Bilder laden darf. Gelingt es einem Angreifer, ein Skript in eine Seite einzuschleusen, verweigert der Browser dessen Ausführung, sofern es nicht aus einer erlaubten Quelle stammt.
Ab Django 6.0 können Sie eine CSP mit der integrierten ContentSecurityPolicyMiddleware von Django festlegen.
Um sie für LocalLibrary zu aktivieren, öffnen Sie /locallibrary/settings.py und fügen Sie die Middleware am Ende der Liste MIDDLEWARE hinzu:
MIDDLEWARE = [
# …
'django.middleware.csp.ContentSecurityPolicyMiddleware',
]
Definieren Sie anschließend die Richtlinie über die Einstellung SECURE_CSP:
# Content Security Policy: only allow resources from this site,
# plus the Bootstrap stylesheet from the jsDelivr CDN.
from django.utils.csp import CSP
SECURE_CSP = {
"default-src": [CSP.SELF],
"style-src": [CSP.SELF, "https://cdn.jsdelivr.net"],
}
Die Direktive default-src erlaubt dem Browser, Ressourcen nur von unserer eigenen Website zu laden (CSP.SELF steht für die Quelle 'self').
Die Direktive style-src erlaubt zusätzlich Stylesheets von dem CDN, das das in unserem Basis-Template verwendete Bootstrap-CSS bereitstellt.
Da diese Richtlinie keine Inline-Skripte oder Inline-Styles zulässt, blockiert der Browser auch Inline-<script>-Elemente, style-Attribute und ähnlichen Code, der in die Seite eingebettet ist.
Genau solchen Code schleusen XSS-Angriffe ein.
Deshalb verwenden die LocalLibrary-Templates CSS-Klassen statt style-Attributen.
Wenn Sie ein Inline-Skript benötigen, kann Django dafür eine Nonce erzeugen.
Fügen Sie die Nonce dem Attribut nonce des Skripts hinzu, um dessen Ausführung zu erlauben. Dies wird in Djangos Content Security Policy verwenden beschrieben.
Starten Sie den Entwicklungsserver und öffnen Sie auf einer beliebigen Seite die Entwicklertools Ihres Browsers.
In der Antwort sollten Sie den Header Content-Security-Policy sehen; in der Konsole sollten keine CSP-Fehler erscheinen.
Hinweis:
Mit der Einstellung SECURE_CSP_REPORT_ONLY können Sie eine Richtlinie testen, bevor Sie sie durchsetzen.
Der Browser meldet dann Verstöße in der Konsole, blockiert aber nichts.
Schutz vor Cross-Site Request Forgery (CSRF)
CSRF-Angriffe ermöglichen es einem böswilligen Benutzer, ohne Wissen oder Einwilligung eines anderen Benutzers Aktionen mit dessen Zugangsdaten auszuführen. Stellen Sie sich beispielsweise einen Hacker vor, der zusätzliche Autoren für unsere LocalLibrary anlegen möchte.
Hinweis: Natürlich geht es unserem Hacker dabei nicht ums Geld! Ein ehrgeizigerer Hacker könnte denselben Ansatz auf anderen Websites nutzen, um wesentlich größeren Schaden anzurichten, etwa indem er Geld auf eigene Konten überweist.
Dazu könnte er eine HTML-Datei wie die folgende erstellen. Sie enthält ein Formular zum Anlegen eines Autors (wie das im vorherigen Abschnitt verwendete), das unmittelbar nach dem Laden der Datei abgesendet wird. Anschließend würde er die Datei an alle Bibliothekare senden und ihnen nahelegen, sie zu öffnen (sie enthält ganz bestimmt nur harmlose Informationen!). Öffnet ein angemeldeter Bibliothekar die Datei, wird das Formular mit dessen Zugangsdaten abgesendet und ein neuer Autor angelegt.
<html lang="en">
<body onload="document.EvilForm.submit()">
<form
action="http://127.0.0.1:8000/catalog/author/create/"
method="post"
name="EvilForm">
<label for="id_first_name">First name:</label>
<input
id="id_first_name"
maxlength="100"
name="first_name"
type="text"
value="Mad"
required />
<label for="id_last_name">Last name:</label>
<input
id="id_last_name"
maxlength="100"
name="last_name"
type="text"
value="Man"
required />
<label for="id_date_of_birth">Date of birth:</label>
<input id="id_date_of_birth" name="date_of_birth" type="text" />
<label for="id_date_of_death">Died:</label>
<input
id="id_date_of_death"
name="date_of_death"
type="text"
value="12/10/2016" />
<input type="submit" value="Submit" />
</form>
</body>
</html>
Starten Sie den Web-Entwicklungsserver und melden Sie sich mit Ihrem Superuser-Konto an. Kopieren Sie den obigen Text in eine Datei und öffnen Sie diese anschließend im Browser. Sie sollten einen CSRF-Fehler erhalten, denn Django schützt vor solchen Angriffen!
Der Schutz wird aktiviert, indem Sie das Template-Tag {% csrf_token %} in Ihre Formulardefinition aufnehmen. Dieses Token erscheint dann wie unten gezeigt in Ihrem HTML, mit einem Wert, der für den Benutzer im aktuellen Browser spezifisch ist.
<input
type="hidden"
name="csrfmiddlewaretoken"
value="0QRWHnYVg776y2l66mcvZqp8alrv4lb8S8lZ4ZJUWGZFA5VHrVfL2mpH29YZ39PW" />
Django erzeugt einen benutzer- und browserspezifischen Schlüssel und weist Formulare zurück, die das Feld nicht enthalten oder für den jeweiligen Benutzer und Browser einen falschen Feldwert aufweisen.
Um diesen Angriff durchzuführen, müsste der Hacker nun den CSRF-Schlüssel des betreffenden Benutzers herausfinden und einfügen. Auch die „Gießkannenmethode“, eine schädliche Datei an alle Bibliothekare zu senden und darauf zu hoffen, dass einer sie öffnet, funktioniert nicht mehr, da der CSRF-Schlüssel browserspezifisch ist.
Der CSRF-Schutz von Django ist standardmäßig aktiviert. Verwenden Sie in Ihren Formularen immer das Template-Tag {% csrf_token %} und für Anfragen, die Daten in der Datenbank ändern oder hinzufügen könnten, POST.
Weitere Schutzmechanismen
Django bietet außerdem weitere Schutzmechanismen, von denen sich die meisten nur schwer demonstrieren ließen oder deren Demonstration wenig aufschlussreich wäre:
- Schutz vor SQL-Injection
-
Sicherheitslücken für SQL-Injection ermöglichen es böswilligen Benutzern, beliebigen SQL-Code in einer Datenbank auszuführen. Dadurch können sie unabhängig von ihren Berechtigungen auf Daten zugreifen sowie diese ändern oder löschen. In fast allen Fällen greifen Sie über die QuerySets und Modelle von Django auf die Datenbank zu. Der zugrunde liegende Datenbanktreiber maskiert das daraus erzeugte SQL daher ordnungsgemäß. Wenn Sie Raw Queries oder eigenes SQL schreiben müssen, sollten Sie ausdrücklich darauf achten, SQL-Injection zu verhindern.
- Schutz vor Clickjacking
-
Bei diesem Angriff fängt ein böswilliger Benutzer Klicks ab, die für eine sichtbare Website bestimmt sind, und leitet sie an eine darunter verborgene Seite weiter. Mit dieser Technik könnte beispielsweise eine echte Bankwebsite angezeigt werden, während die Anmeldedaten über ein unsichtbares, vom Angreifer kontrolliertes
<iframe>abgefangen werden. Django bietet Clickjacking-Schutz durch dieX-Frame-Options-Middleware. Sie kann in Browsern, die diese Funktion unterstützen, verhindern, dass eine Website innerhalb eines Frames dargestellt wird. - TLS/HTTPS erzwingen
-
TLS/HTTPS kann auf dem Webserver aktiviert werden, um den gesamten Datenverkehr zwischen Website und Browser zu verschlüsseln. Dazu gehören auch Anmeldedaten, die andernfalls im Klartext übertragen würden (die Aktivierung von HTTPS wird dringend empfohlen). Wenn HTTPS aktiviert ist, stellt Django weitere Schutzmechanismen bereit:
- Mit
SECURE_PROXY_SSL_HEADERlässt sich prüfen, ob Inhalte über eine sichere Verbindung übertragen werden, auch wenn sie über einen Nicht-HTTP-Proxy eingehen. SECURE_SSL_REDIRECTleitet alle HTTP-Anfragen an HTTPS weiter.- Verwenden Sie HTTP Strict Transport Security (HSTS). Dieser HTTP-Header teilt dem Browser mit, dass er für alle künftigen Verbindungen zu einer bestimmten Website stets HTTPS verwenden soll. Zusammen mit der Weiterleitung von HTTP-Anfragen an HTTPS stellt diese Einstellung sicher, dass nach einer erfolgreichen Verbindung immer HTTPS verwendet wird. HSTS kann entweder über
SECURE_HSTS_SECONDSundSECURE_HSTS_INCLUDE_SUBDOMAINSoder auf dem Webserver konfiguriert werden. - Verwenden Sie „sichere“ Cookies, indem Sie
SESSION_COOKIE_SECUREundCSRF_COOKIE_SECUREaufTruesetzen. Dadurch werden Cookies ausschließlich über HTTPS gesendet.
- Mit
- Validierung des Host-Headers
-
Verwenden Sie
ALLOWED_HOSTS, um nur Anfragen für vertrauenswürdige Hosts anzunehmen.
Es gibt viele weitere Schutzmechanismen und Einschränkungen bei der Verwendung der oben genannten Verfahren. Wir hoffen, Ihnen einen Überblick über die Möglichkeiten von Django gegeben zu haben. Lesen Sie dennoch die Sicherheitsdokumentation von Django.
Zusammenfassung
Django bietet wirksamen Schutz vor einer Reihe häufiger Bedrohungen, darunter XSS- und CSRF-Angriffe. In diesem Artikel haben wir anhand unserer LocalLibrary-Website gezeigt, wie Django mit diesen Bedrohungen umgeht, und als zusätzliche Abwehr gegen XSS eine Content Security Policy hinzugefügt. Außerdem haben wir einige weitere Schutzmechanismen kurz vorgestellt.
Dies war nur ein kurzer Ausflug in die Websicherheit. Wir empfehlen Ihnen dringend, Sicherheit in Django zu lesen, um ein tieferes Verständnis zu erlangen.
Der nächste und letzte Schritt in diesem Django-Modul besteht darin, die Bewertungsaufgabe abzuschließen.