PEP 504 – Standardmäßig das System-RNG verwenden
- Autor:
- Alyssa Coghlan <ncoghlan at gmail.com>
- Status:
- Zurückgezogen
- Typ:
- Standards Track
- Erstellt:
- 15. Sep 2015
- Python-Version:
- 3.6
- Post-History:
- 15. Sep 2015
Inhaltsverzeichnis
- Zusammenfassung
- Rücknahme eines PEP
- Vorschlag
- Begründung
- Diskussion
- Warum "ensure_repeatable" statt "ensure_deterministic"?
- Nur Änderung des Standardwerts für Python 3.6+
- Beibehaltung der Modul-Level-Funktionen
- Warnung bei impliziter Zustimmung zum deterministischen RNG
- Vermeidung der Einführung eines User-Space-CSPRNG
- Ist der deterministische PRNG "sicher genug"?
- Sicherheitsermüdung im Python-Ökosystem
- Danksagungen
- Referenzen
- Urheberrecht
Zusammenfassung
Python verwendet derzeit standardmäßig den deterministischen Mersenne-Twister-Zufallszahlengenerator für die Modul-Level-APIs im random-Modul. Dies erfordert, dass Benutzer wissen, dass sie bei "sicherheitsrelevanten" Arbeiten stattdessen auf die kryptografisch sicheren Schnittstellen os.urandom oder random.SystemRandom oder eine Drittanbieterbibliothek wie cryptography umsteigen sollten.
Leider hat dieser Ansatz zu einer Situation geführt, in der Entwickler, die sich nicht bewusst sind, dass sie sicherheitsrelevante Arbeiten durchführen, die standardmäßigen Modul-Level-APIs verwenden und somit ihre Benutzer unnötigen Risiken aussetzen.
Dies ist kein akutes Problem, sondern ein chronisches. Die oft langen Verzögerungen zwischen der Einführung von Sicherheitslücken und deren Ausnutzung bedeuten, dass es für Entwickler schwierig ist, natürlich aus Erfahrungen zu lernen.
Um eine eventually allumfassende Lösung für das Problem zu bieten, schlägt diese PEP vor, dass Python in Python 3.6 standardmäßig den System-Zufallszahlengenerator verwendet und Entwickler dazu verpflichtet, prozessweit dem deterministischen Zufallszahlengenerierungsverfahren zuzustimmen, entweder durch Verwendung einer neuen random.ensure_repeatable() API oder durch explizites Erstellen einer eigenen random.Random()-Instanz.
Um die Auswirkungen auf bestehenden Code zu minimieren, werden Modul-Level-APIs, die Determinismus erfordern, implizit auf den deterministischen PRNG umgestellt.
Rücknahme eines PEP
Während der Diskussion dieser PEP schlug Steven D'Aprano die einfachere Alternative vor, ein standardisiertes secrets-Modul anzubieten, das einen "eindeutigen Weg" für die Handhabung von sicherheitsrelevanten Aufgaben wie der Generierung von Standardpasswörtern und anderen Tokens bietet.
Stevens Vorschlag hat den gewünschten Effekt, den einfachen Weg zur Generierung solcher Tokens und den richtigen Weg zu deren Generierung abzugleichen, ohne Kompatibilitätsrisiken für die bestehende random-Modul-API einzuführen. Daher wurde diese PEP zugunsten weiterer Arbeit zur Verfeinerung von Stevens Vorschlag als PEP 506 zurückgezogen.
Vorschlag
Derzeit ist es niemals korrekt, die Modul-Level-Funktionen im random-Modul für sicherheitsrelevante Anwendungen zu verwenden. Diese PEP schlägt vor, diese Mahnung in Python 3.6+ so zu ändern, dass es nicht korrekt ist, die Modul-Level-Funktionen im random-Modul für sicherheitsrelevante Anwendungen zu verwenden, wenn random.ensure_repeatable() jemals (direkt oder indirekt) in diesem Prozess aufgerufen wird.
Um dies zu erreichen, würden die Modul-Level-Aufrufbaren im random-Modul nicht mehr an eine random.Random-Instanz gebundene Methoden sein, wie sie es heute sind, sondern Funktionen, die an die entsprechende Methode des bestehenden random._inst-Modulattributs delegieren.
Standardmäßig wird dieses Attribut an eine random.SystemRandom-Instanz gebunden.
Eine neue random.ensure_repeatable() API wird dann das random._inst-Attribut an eine system.Random-Instanz binden und somit das gleiche Verhalten der Modul-Level-API wie in früheren Python-Versionen wiederherstellen (abgesehen von der zusätzlichen Indirektion).
def ensure_repeatable():
"""Switch to using random.Random() for the module level APIs
This switches the default RNG instance from the cryptographically
secure random.SystemRandom() to the deterministic random.Random(),
enabling the seed(), getstate() and setstate() operations. This means
a particular random scenario can be replayed later by providing the
same seed value or restoring a previously saved state.
NOTE: Libraries implementing security sensitive operations should
always explicitly use random.SystemRandom() or os.urandom in order to
correctly handle applications that call this function.
"""
if not isinstance(_inst, Random):
_inst = random.Random()
Um die Auswirkungen auf bestehenden Code zu minimieren, ruft der Aufruf einer der folgenden Modul-Level-Funktionen implizit random.ensure_repeatable() auf.
random.seedrandom.getstaterandom.setstate
Es werden keine Änderungen an den Klassen-APIs random.Random oder random.SystemRandom vorgeschlagen. Anwendungen, die explizit eigene Zufallszahlengeneratoren instanziieren, sind von diesem Vorschlag nicht betroffen.
Warnung bei impliziter Zustimmung
In Python 3.6 gibt die implizite Zustimmung zur Verwendung des deterministischen PRNG eine Deprecation-Warnung aus, die folgende Prüfung verwendet.
if not isinstance(_inst, Random):
warnings.warn(DeprecationWarning,
"Implicitly ensuring repeatability. "
"See help(random.ensure_repeatable) for details")
ensure_repeatable()
Der genaue Wortlaut der Warnung sollte mit einer geeigneten Antwort auf Stack Overflow versehen werden, so wie es für die benutzerdefinierte Fehlermeldung beim Aufruf von print ohne Klammern geschehen ist [10].
In der ersten Python 3-Version, nachdem Python 2.7 in den reinen Sicherheitsfix-Modus wechselt, wird die Deprecation-Warnung zu einer RuntimeWarning hochgestuft, damit sie standardmäßig sichtbar ist.
Diese PEP schlägt nicht vor, die Möglichkeit abzuschaffen, sicherzustellen, dass der standardmäßig verwendete RNG prozessweit ein deterministischer PRNG ist, der bei gegebenem Seed die gleiche Ausgabereihe erzeugt. Diese Funktionalität wird in Modellierungs- und Simulationsszenarien häufig verwendet, und die Anforderung, dass ensure_repeatable() entweder direkt oder indirekt aufgerufen wird, ist eine ausreichende Verbesserung, um die Fälle zu lösen, in denen die Modul-Level-Random-API in Webanwendungen für sicherheitsrelevante Aufgaben ohne angemessene Berücksichtigung der potenziellen Sicherheitsimplikationen der Verwendung eines deterministischen PRNG verwendet wird.
Performance-Auswirkungen
Aufgrund des großen Leistungsunterschieds zwischen random.Random und random.SystemRandom werden Anwendungen, die auf Python 3.6 portiert werden, in Fällen, in denen
- die Anwendung die Modul-Level-Random-API verwendet
- kryptografische Qualität der Zufälligkeit nicht benötigt wird
- die Anwendung nicht bereits implizit durch Aufruf von
random.seed,random.getstateoderrandom.setstateauf den deterministischen PRNG zurückschaltet - die Anwendung nicht aktualisiert wird, um explizit
random.ensure_repeatableaufzurufen
eine erhebliche Leistungsregression erfahren. Dies würde im Abschnitt "Porting" des Python 3.6 "What's New"-Leitfadens vermerkt werden, mit der Empfehlung, den folgenden Code im __main__-Modul betroffener Anwendungen aufzunehmen.
if hasattr(random, "ensure_repeatable"):
random.ensure_repeatable()
Anwendungen, die kryptografisch hochwertige Zufälligkeit benötigen, sollten unabhängig von Geschwindigkeitsüberlegungen den System-Zufallszahlengenerator verwenden. In diesen Fällen behebt die in dieser PEP vorgeschlagene Änderung einen zuvor latenten Sicherheitsfehler.
Änderungen an der Dokumentation
Die Dokumentation des random-Moduls würde aktualisiert werden, um die Dokumentation der Schnittstellen seed, getstate und setstate weiter unten im Modul zu verschieben, zusammen mit der Dokumentation der neuen Funktion ensure_repeatable und der zugehörigen Sicherheitswarnung.
Dieser Abschnitt der Moduldokumentation würde auch eine Diskussion der jeweiligen Anwendungsfälle für den deterministischen PRNG, der durch ensure_repeatable ermöglicht wird (Spiele, Modellierung und Simulation, Softwaretests) und den System-RNG, der standardmäßig verwendet wird (Kryptografie, Generierung von Sicherheitstokens), enthalten. Diese Diskussion würde auch die Verwendung von Drittanbieter-Sicherheitsbibliotheken für letztere Aufgabe empfehlen.
Begründung
Das Schreiben sicherer Software unter Zeit- und Budgetdruck ist ein schwieriges Problem. Dies spiegelt sich in regelmäßigen Benachrichtigungen über Datenpannen mit persönlich identifizierbaren Informationen [1] sowie in Versäumnissen, Sicherheitsaspekte bei der Vernetzung neuer Systeme, wie Kraftfahrzeuge [2], zu berücksichtigen, wider. Es ist auch so, dass viele im Internet leicht verfügbare Programmierratschläge [#search] die mathematischen Arkanata der Computersicherheit nicht berücksichtigen. Diese Probleme werden dadurch verschärft, dass Verteidiger *alle* ihre potenziellen Schwachstellen abdecken müssen, da ein einziger Fehler andere Abwehrmaßnahmen untergraben kann [11].
Einer der Faktoren, die diesen letzten Aspekt besonders schwierig machen, sind APIs, bei denen unsachgemäße Verwendung zu einem *stillen* Sicherheitsfehler führt – einem, bei dem der einzige Weg, um herauszufinden, dass das eigene Handeln falsch ist, darin besteht, dass jemand den eigenen Code überprüft und sagt: "Das ist ein potenzielles Sicherheitsproblem" oder dass ein System, für das man verantwortlich ist, durch eine solche Übersehen kompromittiert wird (und man ist nicht nur weiterhin für dieses System verantwortlich, wenn es kompromittiert wird, sondern die eigenen Einbruchserkennungs- und Auditierungsmechanismen sind gut genug, um nach dem Ereignis festzustellen, wie die Kompromittierung stattgefunden hat).
Diese Art von Situation trägt erheblich zur "Sicherheitsermüdung" bei, bei der Entwickler (oft zu Recht [9]) das Gefühl haben, dass Sicherheitstechniker ständig sagen: "Tun Sie das nicht auf die einfache Weise, das führt zu einer Sicherheitslücke".
Als Designer einer der weltweit beliebtesten Sprachen [8] können wir dieses Problem reduzieren, indem wir in mehr Fällen den einfachen Weg zum richtigen Weg (oder zumindest zum "nicht falschen" Weg) machen, damit Entwickler und Sicherheitstechniker mehr Zeit damit verbringen können, sich um die Eindämmung tatsächlich interessanter Bedrohungen zu kümmern, und weniger Zeit mit der Bekämpfung von Standardverhalten der Sprache.
Diskussion
Warum "ensure_repeatable" statt "ensure_deterministic"?
Dies ist ein Fall, in dem die Bedeutung eines Wortes als Fachjargon mit der typischen Bedeutung des Wortes kollidiert, obwohl es *technisch* dasselbe ist.
Aus technischer Sicht bedeutet ein "deterministischer RNG", dass man bei Kenntnis des Algorithmus und des aktuellen Zustands zuverlässig beliebige zukünftige Zustände berechnen kann.
Das Problem ist, dass "deterministisch" allein diese Qualifikationen nicht vermittelt, so dass es wahrscheinlich stattdessen von Leuten, die die konventionelle Bedeutung kennen, aber nicht mit den zusätzlichen Qualifikationen der technischen Bedeutung vertraut sind, als "vorhersehbar" oder "nicht zufällig" interpretiert wird.
Ein zweites Problem mit "deterministisch" als Beschreibung für den traditionellen RNG ist, dass es nicht wirklich angibt, was man mit dem traditionellen RNG tun kann, was man nicht mit dem System-RNG tun kann.
"ensure_repeatable" zielt darauf ab, beide Probleme zu lösen, da seine gebräuchliche Bedeutung den Hauptgrund für die Bevorzugung des deterministischen PRNG gegenüber dem System-RNG genau beschreibt: die Sicherstellung, dass dieselbe Reihe von Ausgaben durch Angabe desselben Seed-Werts oder durch Wiederherstellung eines zuvor gespeicherten PRNG-Zustands wiederholt werden kann.
Nur Änderung des Standardwerts für Python 3.6+
Einige andere neuere Sicherheitsänderungen, wie die Verbesserung der Fähigkeiten des ssl-Moduls und die standardmäßige Umstellung auf die korrekte Überprüfung von HTTPS-Zertifikaten, wurden als so kritisch erachtet, dass die Änderung in alle derzeit unterstützten Python-Versionen zurückportiert wurde.
Der Unterschied in diesem Fall liegt im Grad – die zusätzlichen Vorteile, diese Änderung ein oder zwei Jahre früher als ohnehin schon auszurollen, rechtfertigen weder den zusätzlichen Aufwand noch die Stabilitätsrisiken, die mit einer so invasiven Änderung in einer Wartungsversion verbunden sind.
Beibehaltung der Modul-Level-Funktionen
Neben allgemeinen Überlegungen zur Abwärtskompatibilität wird Python häufig zu Bildungszwecken verwendet, und wir möchten insbesondere nicht die Vielzahl von Lehrmaterialien ungültig machen, die die Verfügbarkeit der aktuellen random-Modul-API voraussetzen. Daher stellt dieser Vorschlag sicher, dass die meisten öffentlichen APIs nicht nur ohne Änderungen, sondern auch ohne neue Warnungen weiterverwendet werden können.
Warnung bei impliziter Zustimmung zum deterministischen RNG
Es ist notwendig, sich implizit für den deterministischen PRNG zu entscheiden, da Python häufig für Modellierungs- und Simulationszwecke verwendet wird, wo dies das Richtige ist, und in vielen Fällen werden diese Softwaremodelle kein dediziertes Wartungsteam haben, das dafür zuständig ist, sicherzustellen, dass sie mit den neuesten Python-Versionen kompatibel bleiben.
Leider erscheint die explizite Übergabe von random.seed mit Daten aus os.urandom ebenfalls als Fehler in einer Reihe von fehlerhaften Anleitungen zum "Erzeugen eines Sicherheitstokens in Python", die online leicht verfügbar sind.
Die Verwendung von zunächst einer DeprecationWarning und später einer RuntimeWarning, um von der impliziten Umstellung auf den deterministischen PRNG abzuraten, zielt darauf ab, zukünftige Benutzer, die einen kryptografisch sicheren RNG benötigen, vom Aufruf von random.seed() wegzulenken, und diejenigen, die tatsächlich einen deterministischen Generator benötigen, dazu anzuleiten, explizit random.ensure_repeatable() aufzurufen.
Vermeidung der Einführung eines User-Space-CSPRNG
Die ursprüngliche Diskussion dieses Vorschlags auf python-ideas[#csprng]_ schlug die Einführung eines kryptografisch sicheren Pseudo-Zufallszahlengenerators vor und dessen Verwendung als Standard, anstatt standardmäßig den relativ langsamen System-Zufallszahlengenerator zu verwenden.
Das Problem [7] bei diesem Ansatz ist, dass er einen zusätzlichen Fehlerpunkt in sicherheitsrelevanten Situationen einführt, für Anwendungen, bei denen die Zufallszahlengenerierung möglicherweise nicht einmal auf einem kritischen Leistungspfad liegt.
Anwendungen, die kryptografisch hochwertige Zufälligkeit benötigen, sollten unabhängig von Geschwindigkeitsüberlegungen den System-Zufallszahlengenerator verwenden. In diesen Fällen.
Ist der deterministische PRNG "sicher genug"?
Mit einem Wort: "Nein" – deshalb gibt es in der Moduldokumentation eine Warnung, die besagt, dass sie nicht für sicherheitsrelevante Zwecke verwendet werden sollte. Obwohl wir derzeit keine Studien zur Python-Zufallszahlengenerierung speziell kennen, haben Studien zur PHP-Zufallszahlengenerierung [3] gezeigt, dass Schwächen in diesem Subsystem zur Erleichterung eines praktischen Angriffs auf Passwort-Wiederherstellungstokens in beliebten PHP-Webanwendungen ausgenutzt werden können.
Allerdings lautet eine der Regeln für die sichere Softwareentwicklung: "Angriffe werden nur besser, niemals schlechter", so dass es sein kann, dass bis zur Veröffentlichung von Python 3.6 tatsächlich ein praktischer Angriff auf Pythons deterministischen PRNG öffentlich dokumentiert wird.
Sicherheitsermüdung im Python-Ökosystem
In den letzten Jahren hat die gesamte Computerbranche concerted effort unternommen, die gemeinsame Netzwerkinfrastruktur, auf die wir alle angewiesen sind, auf einen "sicher per Standard"-Ansatz aufzurüsten. Als eine der am weitesten verbreiteten Programmiersprachen für die Entwicklung von Netzwerkdiensten (einschließlich der OpenStack Infrastructure-as-a-Service-Plattform) und für die Systemadministration auf Linux-Systemen im Allgemeinen ist ein gerechter Anteil dieser Last auf das Python-Ökosystem gefallen, was für Pythonistas, die Python in anderen Kontexten verwenden, wo diese Probleme nicht so sehr ins Gewicht fallen, verständlicherweise frustrierend ist.
Diese Überlegung ist einer der Hauptfaktoren, die die substanziellen Verbesserungen der Abwärtskompatibilität in diesem Vorschlag im Vergleich zum ursprünglichen Entwurfskonzept, das auf python-ideas veröffentlicht wurde [6], antreiben.
Danksagungen
- Theo de Raadt, dafür, den Vorschlag an Guido van Rossum gemacht zu haben, ernsthaft die Standardisierung auf einen kryptografisch sicheren Zufallszahlengenerator in Betracht zu ziehen.
- Serhiy Storchaka, Terry Reedy, Petr Viktorin und alle anderen in den python-ideas-Threads, die den Ansatz vorschlugen, transparent auf die
random.Random-Implementierung umzuschalten, wenn eine der Funktionen aufgerufen wird, die nur für einen deterministischen RNG sinnvoll sind. - Nathaniel Smith, für die Referenz zu praktischen Angriffen auf die Zufallszahlengenerierung von PHP bei der Generierung von Passwort-Reset-Tokens.
- Donald Stufft, für die weiterführenden Diskussionen mit Netzwerksicherheitsexperten, die vorschlugen, dass die Einführung eines User-Space-CSPRNG zu zusätzlicher Komplexität bei unzureichendem Gewinn im Vergleich zur direkten Verwendung des System-RNG führen würde.
- Paul Moore, für die eloquente Darstellung des aktuellen Sicherheitsermüdungsgrads im Python-Ökosystem.
Referenzen
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0504.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT