Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 506 – Hinzufügen eines Moduls für Geheimnisse zur Standardbibliothek

Autor:
Steven D’Aprano <steve at pearwood.info>
Status:
Final
Typ:
Standards Track
Erstellt:
19-Sep-2015
Python-Version:
3.6
Post-History:


Inhaltsverzeichnis

Zusammenfassung

Dieses PEP schlägt die Aufnahme eines Moduls für allgemeine sicherheitsrelevante Funktionen wie die Generierung von Tokens in die Python-Standardbibliothek vor.

Definitionen

Einige gebräuchliche Abkürzungen, die in diesem Vorschlag verwendet werden

  • PRNG

    Pseudo-Zufallszahlengenerator. Ein deterministischer Algorithmus, der verwendet wird, um zufällig aussehende Zahlen mit bestimmten wünschenswerten statistischen Eigenschaften zu erzeugen.

  • CSPRNG

    Kryptografisch sicherer Pseudo-Zufallszahlengenerator. Ein Algorithmus, der verwendet wird, um zufällig aussehende Zahlen zu erzeugen, die resistent gegen Vorhersagen sind.

  • MT

    Mersenne Twister. Ein intensiv untersuchter PRNG, der derzeit vom random-Modul als Standard verwendet wird.

Begründung

Dieser Vorschlag wird durch Bedenken motiviert, dass die Standardbibliothek von Python es Entwicklern zu einfach macht, versehentlich schwerwiegende Sicherheitsfehler zu machen. Theo de Raadt, der Gründer von OpenBSD, kontaktierte Guido van Rossum und äußerte einige Bedenken [1] bezüglich der Verwendung von MT zur Generierung sensibler Informationen wie Passwörtern, sicheren Tokens, Sitzungsschlüsseln und Ähnlichem.

Obwohl die Dokumentation des random-Moduls ausdrücklich besagt, dass der Standard nicht für Sicherheitszwecke geeignet ist [2], wird stark davon ausgegangen, dass diese Warnung von vielen Python-Entwicklern übersehen, ignoriert oder missverstanden wird. Insbesondere

  • Entwickler haben die Dokumentation möglicherweise nicht gelesen und folglich die Warnung nicht gesehen;
  • sie erkennen möglicherweise nicht, dass ihre spezifische Verwendung des Moduls Auswirkungen auf die Sicherheit hat; oder
  • sie erkennen kein Problem und haben Code (oder Techniken) von Websites kopiert, die keine Best Practices bieten.

Der erste [3] Treffer bei der Suche nach „python how to generate passwords“ auf Google ist ein Tutorial, das die Standardfunktionen des random-Moduls verwendet [4]. Obwohl es nicht für die Verwendung in Webanwendungen gedacht ist, werden wahrscheinlich ähnliche Techniken in dieser Situation verwendet. Der zweite Treffer führt zu einer StackOverflow-Frage zur Generierung von Passwörtern [5]. Die meisten gegebenen Antworten, einschließlich der akzeptierten, verwenden die Standardfunktionen. Als ein Benutzer warnte, dass der Standard leicht kompromittiert werden könnte, wurde ihm gesagt: „Ich glaube, Sie machen sich zu viele Sorgen.“ [6]

Dies deutet stark darauf hin, dass das bestehende random-Modul eine verlockende Ablenkung darstellt, wenn es um die Generierung von z. B. Passwörtern oder sicheren Tokens geht.

Zusätzliche Motivation (philosophischer Natur) findet sich in dem Beitrag, der diese Idee erstmals vorschlug [7].

Vorschlag

Alternative Vorschläge konzentrierten sich auf den Standard-PRNG im random-Modul mit dem Ziel, „standardmäßig sichere“ kryptografisch starke Primitiven bereitzustellen, auf denen Entwickler aufbauen können, ohne über Sicherheit nachzudenken. (Siehe Alternativen unten.) Dies schlägt einen anderen Ansatz vor

  • Die Standardbibliothek bietet bereits kryptografisch starke Primitiven, aber viele Benutzer wissen nicht, dass sie existieren oder wann sie verwendet werden sollen.
  • Anstatt Krypto-naiven Benutzern das Schreiben von sicherem Code zu überlassen, sollte die Standardbibliothek eine Reihe von sofort einsatzbereiten „Batterien“ für die häufigsten Bedürfnisse enthalten, wie z. B. die Generierung sicherer Tokens. Dieser Code wird sowohl einen Bedarf direkt decken („Wie generiere ich einen Token zur Passwortwiederherstellung?“), als auch als Beispiel für akzeptable Praktiken dienen, von denen Entwickler lernen können [8].

Um dies zu erreichen, schlägt dieses PEP vor, ein neues Modul zur Standardbibliothek hinzuzufügen, mit dem vorgeschlagenen Namen secrets. Dieses Modul wird eine Reihe von sofort einsatzbereiten Funktionen für gängige Aktivitäten mit Sicherheitsimplikationen sowie einige Low-Level-Primitive enthalten.

Der Vorschlag ist, dass secrets zum Standardmodul für alles wird, was geheim bleiben soll (Passwörter, Tokens usw.), während random abwärtskompatibel bleibt.

API und Implementierung

Dieses PEP schlägt die folgenden Funktionen für das secrets-Modul vor

  • Funktionen zur Generierung von Tokens, die für die Verwendung in (z. B.) Passwortwiederherstellung, als Sitzungsschlüssel usw. geeignet sind, in den folgenden Formaten
    • als Bytes, secrets.token_bytes;
    • als Text, mit hexadezimalen Ziffern, secrets.token_hex;
    • als Text, mit URL-sicherer Base-64-Kodierung, secrets.token_urlsafe.
  • Eine begrenzte Schnittstelle zum System-CSPRNG, entweder über os.urandom direkt oder über random.SystemRandom. Im Gegensatz zum random-Modul müssen hier keine Methoden zum Seeding, Abrufen oder Einstellen des Zustands oder nicht-uniforme Verteilungen bereitgestellt werden. Es sollten die folgenden Funktionen bereitgestellt werden
    • Eine Funktion zum Auswählen von Elementen aus einer Sequenz, secrets.choice.
    • Eine Funktion zum Generieren einer bestimmten Anzahl von zufälligen Bits und/oder Bytes als Integer, secrets.randbits.
    • Eine Funktion zum Zurückgeben eines zufälligen Integers im halboffenen Bereich 0 bis zum gegebenen oberen Limit, secrets.randbelow [9].
  • Eine Funktion zum Vergleichen von Text- oder Byte-Digests auf Gleichheit, während sie resistent gegen Timing-Angriffe ist, secrets.compare_digest.

Der Konsens scheint zu sein, dass kein neuer CSPRNG zum random-Modul hinzugefügt werden muss, um diese Verwendungen zu unterstützen; SystemRandom wird ausreichend sein.

Einige illustrative Implementierungen wurden von Alyssa (Nick) Coghlan [10] und eine minimalistische API von Tim Peters [11] bereitgestellt. Diese Idee wurde auch im Issue Tracker des Moduls „cryptography“ diskutiert [12]. Der folgende Pseudocode sollte als Ausgangspunkt für die tatsächliche Implementierung betrachtet werden

from random import SystemRandom
from hmac import compare_digest

_sysrand = SystemRandom()

randbits = _sysrand.getrandbits
choice = _sysrand.choice

def randbelow(exclusive_upper_bound):
    return _sysrand._randbelow(exclusive_upper_bound)

DEFAULT_ENTROPY = 32  # bytes

def token_bytes(nbytes=None):
    if nbytes is None:
        nbytes = DEFAULT_ENTROPY
    return os.urandom(nbytes)

def token_hex(nbytes=None):
    return binascii.hexlify(token_bytes(nbytes)).decode('ascii')

def token_urlsafe(nbytes=None):
    tok = token_bytes(nbytes)
    return base64.urlsafe_b64encode(tok).rstrip(b'=').decode('ascii')

Das secrets-Modul selbst wird reines Python sein, und andere Python-Implementierungen können es leicht unverändert verwenden oder nach Bedarf anpassen. Eine Implementierung ist auf BitBucket verfügbar [13].

Standardargumente

Eine schwierige Frage ist: „Wie viele Bytes sollte mein Token haben?“. Wir können bei dieser Frage helfen, indem wir einen Standardwert für die Entropie für die „token_*“-Funktionen bereitstellen. Wenn das Argument nbytes None ist oder nicht angegeben wird, wird die Standardentropie verwendet. Dieser Standardwert sollte groß genug sein, um für mittelsichere Anwendungen sicher zu sein, wird sich aber in Zukunft ändern, möglicherweise sogar in einem Wartungs-Release [14].

Namenskonventionen

Eine Frage sind die Namenskonventionen im Modul [15], ob C-ähnliche Namenskonventionen wie „randrange“ oder pythonischere Namen wie „random_range“ verwendet werden sollen.

Funktionen, die einfach gebundene Methoden der privaten SystemRandom-Instanz sind (z. B. randrange) oder ein dünner Wrapper darum, sollten die bekannten Namen beibehalten. Diejenigen, die etwas Neues sind (wie die verschiedenen token_*-Funktionen), werden pythonischere Namen verwenden.

Alternativen

Eine Alternative besteht darin, den Standard-PRNG des random-Moduls zu ändern [16]. Dies stieß auf erhebliche Skepsis und offenen Widerstand

  • Es besteht die Befürchtung, dass ein CSPRNG langsamer sein könnte als der aktuelle PRNG (der im Fall von MT bereits recht langsam ist).
  • Einige Anwendungen (wie wissenschaftliche Simulationen und das Wiederholen von Gameplay) erfordern die Möglichkeit, den PRNG in einen bekannten Zustand zu versetzen, was ein CSPRNG-Design naturgemäß nicht bietet.
  • Eine weitere wichtige Verwendung des random-Moduls sind einfache „Rate-eine-Zahl“-Spiele, die von Anfängern geschrieben werden, und viele Leute sind abgeneigt, Änderungen am random-Modul vorzunehmen, die dies erschweren könnten.
  • Obwohl kein Vorschlag zur Entfernung von MT aus dem random-Modul besteht, gab es erheblichen Widerstand gegen die Idee, auf einen Nicht-CSPRNG oder auf rückwärts inkompatible Änderungen verzichten zu müssen.
  • Demonstrierte Angriffe gegen MT betreffen typischerweise PHP-Anwendungen. Es wird angenommen, dass die PHP-Version von MT ein deutlich weicheres Ziel ist als die Python-Version, aufgrund einer schlechten Seed-Technik [17]. Folglich, ohne einen nachgewiesenen Angriff auf Python-Anwendungen, widersetzen sich viele Leute einer rückwärts inkompatiblen Änderung.

Alyssa Coghlan machte einen früheren Vorschlag für einen global konfigurierbaren PRNG, der standardmäßig den System-CSPRNG verwendet, hat ihn aber inzwischen zugunsten dieses Vorschlags zurückgezogen.

Vergleich mit anderen Sprachen

  • PHP

    PHP enthält die Funktion uniqid [18], die standardmäßig einen dreizehn Zeichen langen String zurückgibt, basierend auf der aktuellen Zeit in Mikrosekunden. Übersetzt in Python-Syntax hat sie die folgende Signatur

    def uniqid(prefix='', more_entropy=False)->str
    

    Die PHP-Dokumentation warnt, dass diese Funktion nicht für Sicherheitszwecke geeignet ist. Dennoch verwenden verschiedene etablierte, bekannte PHP-Anwendungen sie zu diesem Zweck (Nachweis erforderlich).

    PHP 5.3 und höher enthält auch die Funktion openssl_random_pseudo_bytes [19]. Übersetzt in Python-Syntax hat sie ungefähr die folgende Signatur

    def openssl_random_pseudo_bytes(length:int)->Tuple[str, bool]
    

    Diese Funktion gibt einen pseudozufälligen Byte-String gegebener Länge zurück und ein boolesches Flag, ob der String als kryptografisch stark gilt. Das PHP-Handbuch legt nahe, dass die Rückgabe von etwas anderem als True selten sein sollte, außer bei alten oder fehlerhaften Plattformen.

  • JavaScript

    Basierend auf einer recht oberflächlichen Suche [20] scheint es keine bekannten Standardfunktionen zur Erzeugung starker Zufallswerte in JavaScript zu geben. Math.random wird oft verwendet, trotz ernsthafter Schwächen, die es für kryptografische Zwecke ungeeignet machen [21]. In den letzten Jahren haben die meisten Browser Unterstützung für window.crypto.getRandomValues erhalten [22].

    Node.js bietet ein reichhaltiges kryptografisches Modul, crypto [23], von dem der Großteil außerhalb des Umfangs dieses PEP liegt. Es enthält eine einzelne Funktion zur Generierung von Zufallsbytes, crypto.randomBytes.

  • Ruby

    Die Ruby-Standardbibliothek enthält ein Modul SecureRandom [24], das die folgenden Methoden enthält

    • base64 – gibt einen Base64-kodierten Zufallsstring zurück.
    • hex – gibt einen zufälligen Hexadezimalstring zurück.
    • random_bytes – gibt einen zufälligen Byte-String zurück.
    • random_number – gibt je nach Argument entweder einen zufälligen Integer im Bereich (0, n) oder eine zufällige Gleitkommazahl zwischen 0,0 und 1,0 zurück.
    • urlsafe_base64 – gibt einen zufälligen URL-sicheren Base64-kodierten String zurück.
    • uuid – gibt eine zufällige Universally Unique Identifier der Version 4 zurück.

Wie soll das Modul heißen?

Es gab einen Vorschlag, ein Untermodul „random.safe“ hinzuzufügen, unter Berufung auf das Zen of Python-Koan „Namespaces are one honking great idea“. Der Autor des Zen, Tim Peters, hat sich jedoch gegen diese Idee ausgesprochen [25] und empfiehlt ein Top-Level-Modul.

In bisherigen Diskussionen in der python-ideas-Mailingliste hat der Name „secrets“ einige Zustimmung und keinen starken Widerspruch erhalten.

Es gibt bereits ein bestehendes Drittanbieter-Modul mit demselben Namen [26], aber es scheint ungenutzt und verlassen zu sein.

Häufig gestellte Fragen

  • F: Ist das ein echtes Problem? Sicher ist MT so zufällig, dass niemand seine Ausgabe vorhersagen kann.

    A: Der Konsens unter Sicherheitsexperten ist, dass MT in sicherheitskritischen Kontexten nicht sicher ist. Es ist nicht schwierig, den internen Zustand von MT zu rekonstruieren [27] [28] und somit alle vergangenen und zukünftigen Werte vorherzusagen. Es gibt eine Reihe bekannter, praktischer Angriffe auf Systeme, die MT für Zufälligkeit verwenden [29].

  • F: Angriffe auf PHP sind eine Sache, aber gibt es bekannte Angriffe auf Python-Software?

    A: Ja. Es gab Schwachstellen in Zope und Plone, zumindest. Hanno Schlichting kommentierte [30]

    "In the context of Plone and Zope a practical attack was
    demonstrated, but I can't find any good non-broken links about
    this anymore.  IIRC Plone generated a random number and exposed
    this on each error page along the lines of 'Sorry, you encountered
    an error, your problem has been filed as <random number>, please
    include this when you contact us'.  This allowed anyone to do large
    numbers of requests to this page and get enough random values to
    reconstruct the MT state.  A couple of security related modules used
    random instead of system random (cookie session ids, password reset
    links, auth token), so the attacker could break all of those."
    

    Christian Heimes meldete dieses Problem dem Zope-Sicherheitsteam im Jahr 2012 [31], es gibt mindestens zwei damit zusammenhängende CVE-Schwachstellen [32] und mindestens einen Workaround für dieses Problem in Django [33].

  • F: Ist das eine Alternative zu spezialisierter kryptografischer Software wie SSL?

    A: Nein. Dies ist eine „Batteries included“-Lösung, kein vollwertiger „Atomreaktor“. Sie soll einige grundlegende Sicherheitsfehler mildern, nicht alle sicherheitsrelevanten Probleme lösen. Um Alyssa Coghlan mit Bezug auf ihren früheren Vorschlag zu zitieren [34]

    "...folks really are better off learning to use things like
    cryptography.io for security sensitive software, so this change
    is just about harm mitigation given that it's inevitable that a
    non-trivial proportion of the millions of current and future
    Python developers won't do that."
    
  • F: Was ist mit einem Passwortgenerator?

    A: Der Konsens ist, dass die Anforderungen an Passwortgeneratoren zu variabel sind, um sie gut in die Standardbibliothek aufzunehmen [35]. Kein Passwortgenerator wird in der Erstveröffentlichung des Moduls enthalten sein, stattdessen wird er in der Dokumentation als Rezept (à la die Rezepte im itertools-Modul) aufgeführt [36].

  • F: Wird secrets /dev/random (das blockiert) oder /dev/urandom (das nicht blockiert) unter Linux verwenden? Was ist mit anderen Plattformen?

    A: secrets wird auf os.urandom und random.SystemRandom basieren, welche Schnittstellen zur besten Quelle kryptografischer Zufälligkeit Ihres Betriebssystems sind. Unter Linux kann das /dev/urandom sein [37], unter Windows kann es CryptGenRandom() sein, aber siehe die Dokumentation und/oder den Quellcode für detaillierte Implementierungsdetails.

Referenzen


Quelle: https://github.com/python/peps/blob/main/peps/pep-0506.rst

Zuletzt geändert: 2025-02-01 08:59:27 GMT