Eigener Code¶
Angriffe auf die Lieferkette gehen nicht nur von Abhängigkeiten aus, auch euer eigener Code kann Angriffspunkte liefern. Ein fest im Quellcode hinterlegtes PyPI-Token liefert, sobald es in ein öffentliches Repository hochgeladen wurde, alles, was für einem Angriff benötigt wird und euer Konto zu kompromittieren und bösartige Pakete unter eurem Namen zu veröffentlichen. Abgesehen von Secrets verbergen sich häufige Sicherheitsfehler in alltäglichen Codemustern, die bei einem Code-Review zunächst unbedenklich erscheinen und von Menschen übersehen werden können. Diese mit einem Linter aufzuspüren, ist die erste Verteidigungsstufe.
Das ewige Geheimnis¶
Durchgesickerte Zugangsdaten sind der Ausgangspunkt für viele Sicherheitsverletzungen in der Lieferkette. Ein offengelegtes PyPI-Token ermöglicht, mit Hintertüren versehene Versionen eurer Pakete zu veröffentlichen. Eine offengelegte Datenbank-URL ermöglicht, Daten zu entwenden. Und doch ist ein solches Muster weit verbreitet. Besser ist die Verwendung von Umgebungsvariablen:
import os
DATABASE_KEY = os.environ["DB_KEY"]
DATABASE_URL = os.environ["DB_URL"]
Warnung
Git vergisst nie: wenn ihr ein Secret einmal durch Git verwaltet habt, bleibt es für immer in der Historie eures Repositories erhalten. Es in einem späteren Commit einfach zu löschen, hilft nicht wirklich. Alle, die Zugriff auf das Repository haben, können diese Anmeldedaten wieder extrahieren. Bei Angriffen wird oft zunächst die Git-Historie nach Geheimnissen durchforstet, und ein einmal veröffentlichts PyPI-Token oder Cloud-Anmeldedaten sind oft der erste Schritt bei einer Kompromittierung der Lieferkette.
Kryptografische Schwachstellen¶
Weitere häufige Sicherheitslücken sind kryptografische Schwachstellen wie MD5 und SHA-1. MD5-Kollisionen wurden erstmals 2004 nachgewiesen und SHA1-Kollisionen 2017. Es können also Kollisionen erzeugt werden durch andere Eingaben, die denselben Hash-Wert ergeben. Dies ermöglicht die Fälschung von Zertifikaten, die Manipulation von Downloads oder die Umgehung von Integritätsprüfungen. Verwendet daher keines der beiden Verfahren für Sicherheitszwecke sondern stattdessen SHA256 oder besser:
import hashlib
digest = hashlib.sha256(payload).hexdigest()
Hängende Verbindungen¶
Das hier ist zwar subtil, aber dennoch gefährlich, da ein langsamer Server euren Prozess auf unbestimmte Zeit zum Stillstand bringen kann. Ein Angriff über einen solchen Server, mit dem eure Anwendung kommuniziert, kann jede Anfrage zum Erliegen bringen, euren Thread-Pool erschöpfen und einen Denial-of-Service-Angriff auslösen. Eure gesamte Anwendung kommt dann zum Stillstand, weil ihr einen Parameter vergessen habt. Daher solltet ihr immer einen Timeout angeben:
>>> import httpx
>>> r = httpx.get("https://httpbin.org/get", timeout=30)
httpx.ReadTimeout: The read operation timed out
Erkennt Sicherheitslücken mit Ruff¶
So erkennen Sie diese mit Ruff
Ruff ist ein schneller Python-Linter, der umfassende Sicherheitsregeln von Bandit enthält:
$ uvx ruff check --select S .
Siehe auch
Weitere Informationen findet ihr in der Dokumentation zu den Ruff-Sicherheitsregeln.
Für zukünftige Checks könnt ihr ruff ihn in der pyproject.toml-Datei
konfigurieren:
[tool.ruff]
lint.select = ["S"]
Die Sicherheitsregeln ["S"] mit den Bandit-Prüfungen.spüren fest codierte
Geheimnisse, schwache Verschlüsselung und unsichere Deserialisierung auf. Dabei
läuft Ruff in weniger als einer Sekunde, sodass ihr es während der Eingabe in
eurer IDE und vor jedem Commit ausführen könnt. Alle drei oben genannten
Schwachstellen werden erkannt und noch viel mehr, u. a.:
Regel |
Beschreibung |
fest codierte Geheimnisse |
|
Pickle und andere unsichere Deserialisierung |
|
Verwendung von |
|
fehlende Zeitüberschreitungen |
|
schwache Kryptografie wie z. B. MD5-Kollisionen |
|
SQL-Injection über String-Formatierung |
Siehe auch
Bandit könnt ihr auch in Jupyter Notebooks, IDEs und prek integrieren.
Zudem könnt ihr Pysa für Taint-Analysen verwenden.
Für GitHub-Repositories könnt ihr alternativ auch CodeQL verwenden; s.a. codeql-action.
Vertrauenswürdige Veröffentlichung¶
In einem früheren Abschnitt haben wir schon einige Hinweise gegeben, wie die Veröffentlichung von Python-Paketen auf PyPI abgesichert werden kann: