Rheinwerk Computing < openbook >

 
Inhaltsverzeichnis
1 Einleitung
2 Die Programmiersprache Python
Teil I Einstieg in Python
3 Erste Schritte im interaktiven Modus
4 Der Weg zum ersten Programm
5 Kontrollstrukturen
6 Dateien
7 Das Laufzeitmodell
8 Funktionen, Methoden und Attribute
9 Informationsquellen zu Python
Teil II Datentypen
10 Das Nichts – NoneType
11 Operatoren
12 Numerische Datentypen
13 Sequenzielle Datentypen
14 Zuordnungen
15 Mengen
16 Collections
17 Datum und Zeit
18 Aufzählungstypen – Enum
Teil III Fortgeschrittene Programmiertechniken
19 Funktionen
20 Modularisierung
21 Objektorientierung
22 Ausnahmebehandlung
23 Iteratoren
24 Kontextobjekte
25 Manipulation von Funktionen und Methoden
Teil IV Die Standardbibliothek
26 Mathematik
27 Kryptografie
28 Reguläre Ausdrücke
29 Schnittstelle zu Betriebssystem und Laufzeitumgebung
30 Kommandozeilenparameter
31 Dateisystem
32 Parallele Programmierung
33 Datenspeicherung
34 Netzwerkkommunikation
35 Debugging und Qualitätssicherung
36 Dokumentation
Teil V Weiterführende Themen
37 Anbindung an andere Programmiersprachen
38 Distribution von Python-Projekten
39 Grafische Benutzeroberflächen
40 Python als serverseitige Programmiersprache im WWW – ein Einstieg in Django
41 Wissenschaftliches Rechnen
42 Insiderwissen
43 Von Python 2 nach Python 3
A Anhang
Stichwortverzeichnis

Download:
- Beispielprogramme, ca. 464 KB

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Python 3 von Johannes Ernesti, Peter Kaiser
Das umfassende Handbuch
Buch: Python 3

Python 3
Pfeil 27 Kryptografie
Pfeil 27.1 Hash-Funktionen – hashlib
Pfeil 27.1.1 Verwendung des Moduls
Pfeil 27.1.2 Weitere Algorithmen
Pfeil 27.1.3 Vergleich großer Dateien
Pfeil 27.1.4 Passwörter
Pfeil 27.2 Verschlüsselung – PyCrypto
Pfeil 27.2.1 Symmetrische Verschlüsselungsverfahren
Pfeil 27.2.2 Asymmetrische Verschlüsselungsverfahren
 
Zum Seitenanfang

27    Kryptografie Zur vorigen ÜberschriftZur nächsten Überschrift

Kryptografische Algorithmen sind ein wichtiger Bestandteil moderner Software. Sie werden zur klassischen symmetrischen oder asymmetrischen Verschlüsselung eingesetzt, beispielsweise um sensible Daten des Benutzers zu schützen. Ein weiterer Anwendungsfall kryptografischer Algorithmen ist das Hashing, bei dem es darum geht, aus komplexen Instanzen, etwa langen Dokumenten, einen kurzen, möglichst kollisionsfreien Hash-Wert zu bestimmen. Dieser Hash-Wert kann dann zum Beispiel zum effizienten Vergleich zweier Dokumente verwendet werden.

Wir werden im Folgenden zunächst das Modul hashlib der Standardbibliothek besprechen, das verschiedene Hash-Funktionen implementiert, und danach das Drittanbietermodul PyCrypto behandeln, das eine umfassende Sammlung kryptografischer Algorithmen bereitstellt.

 
Zum Seitenanfang

27.1    Hash-Funktionen – hashlib Zur vorigen ÜberschriftZur nächsten Überschrift

Das Modul hashlib der Standardbibliothek implementiert die gängigsten Hash-Funktionen. Das sind komplexe Algorithmen, die aus einem Parameter, zumeist einem String, einen Hash-Wert berechnen. Wozu kann ein solcher Hash-Wert verwendet werden?

Stellen Sie sich vor, Sie würden eine Forensoftware entwickeln, die später für eine Community im Internet eingesetzt werden soll. Bevor ein Benutzer Beiträge im Forum verfassen darf, muss er sich mit seinem Benutzernamen und dem dazu passenden Passwort anmelden. Natürlich ist es im Sinne des Forenbetreibers und vor allem des Benutzers selbst, dass das Passwort nicht in falsche Hände gerät. Es stellt sich also die Frage, wie die Anmeldeprozedur möglichst sicher gestaltet werden kann.

Die intuitivste Möglichkeit wäre es, Benutzernamen und Passwort an die Forensoftware zu übermitteln. Dort werden diese beiden Informationen mit den Anmeldedaten aller Benutzer verglichen, und bei einem Treffer wird der Zugang zum Forum ermöglicht.

Würde eine solche Software die Anmeldeprozedur tatsächlich so durchführen, müssten Benutzername und Passwort im Klartext in der internen Datenbank des Forums gespeichert werden. Das ist beim Benutzernamen kein größeres Problem, da es sich dabei im Allgemeinen um eine öffentliche Information handelt. Doch das Passwort im Klartext in einer solchen Datenbank zu speichern, wäre grob fahrlässig. Ein Angreifer, der über eine Sicherheitslücke in einem anderen Teil der Software Zugriff auf die Datenbank erlangt, wäre sofort im Besitz aller Passwörter der angemeldeten Benutzer. Das wird besonders dann brisant, wenn man bedenkt, dass viele Leute das gleiche Passwort für mehrere Benutzerkonten verwenden.

Wünschenswert wäre es also, die Korrektheit eines Passworts mit an Sicherheit grenzender Wahrscheinlichkeit zu ermitteln, ohne Referenzpasswörter im Klartext speichern zu müssen. Und genau hier kommen Hash-Funktionen ins Spiel. Eine Hash-Funktion bekommt einen Parameter übergeben und errechnet daraus den sogenannten Hash-Wert. Wenn sich jetzt ein neuer Benutzer bei der Forensoftware anmeldet und sein Passwort wählt, wird dieses nicht im Klartext in die Datenbank eingetragen, sondern es wird der Hash-Wert des Passworts gespeichert.

Beim Einloggen schickt der Benutzer sein Passwort an den Server. Dieser errechnet dann den Hash-Wert des übertragenen Passworts und vergleicht ihn mit den gespeicherten Hash-Werten.[ 111 ](Da Hash-Funktionen deterministisch sind, ist es für den Angreifer weiterhin möglich, Passwörter auszuprobieren und die Hash-Werte mit den in der Datenbank gespeicherten zu vergleichen. Ein solcher Wörterbuchangriff wird mithilfe eines Salts erschwert. Das ist eine Zufallszahl, die an ein Passwort gehängt wird, bevor dessen Hash-Wert bestimmt wird. )

Damit eine solche Anmeldeprozedur funktioniert und ein potenzieller Angreifer auch mit Zugriff auf die Datenbank keine Passwörter errechnen kann, müssen Hash-Funktionen einige Bedingungen erfüllen:

  • Eine Hash-Funktion stellt eine Einwegcodierung dar. Das heißt, dass die Berechnung des Hash-Wertes nicht umkehrbar ist, man also aus einem Hash-Wert nicht auf den ursprünglichen Parameter schließen kann.
  • Bei Hash-Funktionen treten grundsätzlich sogenannte Kollisionen auf, das sind zwei verschiedene Parameter, die denselben Hash-Wert ergeben. Ein wesentlicher Schritt zum Knacken einer Hash-Funktion ist es, solche Kollisionen berechnen zu können. Eine Hash-Funktion sollte also die Berechnung von Kollisionen so stark erschweren, dass sie nur unter extrem hohem Zeitaufwand zu bestimmen sind.
  • Eine Hash-Funktion sollte möglichst willkürlich sein, sodass man nicht aufgrund eines ähnlichen Hash-Wertes darauf schließen kann, dass man in der Nähe des gesuchten Passworts ist. Sobald der Parameter der Hash-Funktion minimal verändert wird, sollte ein völlig anderer Hash-Wert berechnet werden.
  • Zu guter Letzt sollte eine Hash-Funktion schnell zu berechnen sein. Außerdem müssen sich die entstehenden Hash-Werte untereinander effizient vergleichen lassen.

Das Anwendungsfeld von Hash-Funktionen ist weit gefächert. So werden sie, abgesehen von dem oben genannten Passwortbeispiel, unter anderem auch zum Vergleich großer Dateien verwendet. Anstatt diese Dateien untereinander Byte für Byte zu vergleichen, werden ihre Hash-Werte berechnet und verglichen. Mit den Hash-Werten lässt sich sagen, ob die Dateien mit Sicherheit verschieden oder mit großer Wahrscheinlichkeit identisch sind. Das ist besonders dann interessant, wenn es aufgrund eingeschränkter Bandbreite gar nicht möglich ist, die Dateien direkt zu vergleichen. So ist der Vergleich der Hash-Werte beispielsweise die effizienteste Methode, die Authentizität einer aus dem Internet heruntergeladenen Datei zu überprüfen.

Beachten Sie, dass die Wahrscheinlichkeit einer Kollision bei den im Modul hashlib implementierten Verfahren sehr gering, aber theoretisch immer noch vorhanden ist.

 
Zum Seitenanfang

27.1.1    Verwendung des Moduls Zur vorigen ÜberschriftZur nächsten Überschrift

Zunächst enthält das Modul hashlib eine Reihe von Klassen, die jeweils einen Hash-Algorithmus implementieren:

Klasse Algorithmus Hash-Größe Beschreibung
md5 MD5 128 Bit Message-Digest Algorithm 5
sha1 SHA-1 160 Bit Secure Hash Algorithm 1
sha224 SHA-224 224 Bit Secure Hash Algorithm 224
sha256 SHA-256 256 Bit Secure Hash Algorithm 256
sha384 SHA-384 384 Bit Secure Hash Algorithm 384
sha512 SHA-512 512 Bit Secure Hash Algorithm 512

Tabelle 27.1    Unterstützte Hash-Funktionen

[»]  Hinweis

Beachten Sie, dass die Algorithmen MD5 und SHA-1 bereits ansatzweise gebrochen wurden. Sie sollten daher in sicherheitsrelevanten Anwendungen nicht mehr verwendet werden.

Die Verwendung dieser Klassen ist identisch. Deshalb wird sie hier exemplarisch an der Klasse md5 gezeigt. Beim Instanziieren der Klasse md5 wird eine bytes-Instanz übergeben, deren Hash-Wert berechnet werden soll.

>>> import hashlib
>>> m = hashlib.md5(b"Hallo Welt")

Durch Aufruf der Methode digest wird der berechnete Hash-Wert als Byte-Folge zurückgegeben. Beachten Sie, dass die zurückgegebene bytes-Instanz durchaus nicht druckbare Zeichen enthalten kann.

>>> m.digest()
b'\\7*2\xc9\xaet\x8aL\x04\x0e\xba\xdcQ\xa8)'

Durch Aufruf der Methode hexdigest wird der berechnete Hash-Wert als String zurückgegeben, der eine Folge zweistelliger Hexadezimalzahlen enthält. Diese Hexadezimalzahlen repräsentieren jeweils ein Byte des Hash-Wertes. Der zurückgegebene String enthält ausschließlich druckbare Zeichen.

>>> m.hexdigest()
'5c372a32c9ae748a4c040ebadc51a829'
 
Zum Seitenanfang

27.1.2    Weitere Algorithmen Zur vorigen ÜberschriftZur nächsten Überschrift

Neben den eingangs aufgelisteten Hash-Algorithmen, die garantiert in hashlib vorhanden sind, stellt das Modul eine Reihe weiterer Algorithmen bereit, deren Vorhandensein von den Gegebenheiten des Betriebssystems abhängt. Diese zusätzlichen Algorithmen lassen sich über die Funktion new instanziieren; ihr muss der Name des Algorithmus übergeben werden:

>>> m = hashlib.new("md4", b"Hallo Welt")
>>> m.hexdigest()
'5f7efe84c39847ee689edb9a7848ad74'

Die Menge der insgesamt zur Verfügung stehenden Algorithmen wird über algorithms_available bereitgestellt:

>>> hashlib.algorithms_available
{'SHA', 'mdc2', 'RIPEMD160', 'DSA', 'SHA384', 'sha384', 'shake_128', 'ripemd160', 'blake2s', 'sha3_384', 'sha3_224', 'blake2b',
'ecdsa-with-SHA1', 'dsaEncryption', 'MDC2', 'sha224', 'sha3_256', 'sha1',
'sha3_512', 'shake_256', 'SHA512', 'sha', 'SHA1', 'md5', 'dsaWithSHA', 'md4',
'sha512', 'sha256', 'whirlpool', 'SHA256', 'MD4', 'MD5', 'SHA224', 'DSA-SHA'}
 
Zum Seitenanfang

27.1.3    Vergleich großer Dateien Zur vorigen ÜberschriftZur nächsten Überschrift

Hash-Funktionen berechnen aus einer prinzipiell unbegrenzten Datenmenge einen kurzen Hash-Wert. Aufgrund der Eigenschaften einer Hash-Funktion ist die Wahrscheinlichkeit, zwei verschiedene Datenmengen zu finden, die den gleichen Hash-Wert ergeben, sehr gering. Dadurch eignen sich Hash-Funktionen dazu, große Dateien miteinander zu vergleichen, ohne dass die Dateien an einem gemeinsamen Ort liegen müssen. Auf diese Weise lässt sich beispielsweise feststellen, ob eine auf einem Server gespeicherte Datei neu hochgeladen werden muss, weil sie sich auf dem Rechner des Nutzers verändert hat.

Das folgende Beispielprogramm liest zwei Dateien ein und vergleicht sie anhand ihrer Hash-Werte:

import hashlib
with open("datei1.txt", "rb") as f1, open("datei2.txt", "rb") as f2:
if hashlib.md5(f1.read()).digest() == hashlib.md5(f2.read()).digest():
print("Die Dateien sind gleich")
else:
print("Die Dateien sind verschieden")

In diesem Fall wurde die verbreitete Hash-Funktion md5 verwendet, es können aber auch die anderen in hashlib enthaltenen Funktionen eingesetzt werden.

Für die Arbeit mit Datenströmen enthalten die Hash-Klassen die Methode update, mit deren Hilfe sich die bei der Erzeugung angegebene Datenmenge erweitern lässt:

>>> h1 = hashlib.md5(b"Erstens.")
>>> h1.update(b"Zweitens.")
>>> h1.update(b"Drittens.")
>>>
>>> h2 = hashlib.md5(b"Erstens.Zweitens.Drittens.")
>>> h1.digest() == h2.digest()
True
 
Zum Seitenanfang

27.1.4    Passwörter Zur vorigen ÜberschriftZur nächsten Überschrift

Das folgende Beispielprogramm verwendet das Modul hashlib, um einen Passwortschutz zu realisieren. Das Passwort soll dabei nicht als Klartext im Quelltext gespeichert werden, sondern als Hash-Wert. Dadurch ist gewährleistet, dass die Passwörter nicht einsehbar sind, selbst wenn jemand in den Besitz der Hash-Werte kommen sollte. Auch anmeldepflichtige Internetportale wie beispielsweise Foren speichern die Passwörter der Benutzer als Hash-Werte.

import hashlib
pwhash = "578127b714de227824ab105689da0ed2"
m = hashlib.md5(bytes(input("Ihr Passwort bitte: "), "utf-8"))
if pwhash == m.hexdigest():
print("Zugriff erlaubt")
else:
print("Zugriff verweigert")

Das Programm liest ein Passwort vom Benutzer ein, errechnet den MD5-Hash-Wert dieses Passworts und vergleicht ihn mit dem gespeicherten Hash-Wert. Der vorher berechnete Hash-Wert pwhash ist in diesem Fall im Programm vorgegeben. Unter normalen Umständen stünde er mit anderen Hash-Werten in einer Datenbank oder wäre in einer Datei gespeichert. Wenn beide Werte übereinstimmen, wird symbolisch »Zugriff erlaubt« ausgegeben. Das Passwort für dieses Programm lautet »Mein Passwort«.

Einen Hash-Wert zum Speichern von Passwörtern zu verwenden, ist gängige Praxis. Die bislang besprochenen Hash-Funktionen, darunter insbesondere die oben eingesetzte Funktion md5, eignen sich dazu aber nur bedingt, da sie anfällig gegenüber Brute-Force-Angriffen sind. Damit Passwörter sicher gespeichert werden können, muss eine Hash-Funktion weitere Eigenschaften besitzen:

  • Sie muss einen Salt unterstützen. Das ist eine Zeichenfolge, die an das Passwort angehängt wird, bevor der Hash-Wert berechnet wird. Auf diese Weise können für zwei Benutzer verschiedene Hash-Werte gespeichert werden, selbst wenn sie das gleiche Passwort verwenden. Das verhindert das Knacken von Passwörtern mithilfe vorberechneter Klartexttabellen, sogenannter Rainbow Tables.
  • Sie muss in einer parametrisierbaren Anzahl Runden ablaufen, damit die Rechendauer der Hash-Funktion eingestellt werden kann. Das erschwert das massenhafte Ausprobieren möglicher Passwörter.
  • Speziell für das Speichern von Passwörtern enthält das Modul hashlib die Funktion pbkdf2_hmac[ 112 ](für Password-Based Key Derivation Function 2 ):
  pbkdf2_hmac(name, password, salt, rounds)

Sie berechnet einen Passwort-Hash für das Passwort password mit dem Salt salt unter Verwendung von round Runden des Algorithmus. Der hier implementierte Algorithmus basiert auf einer der grundlegenden Hash-Funktionen, die zu Beginn des Abschnitts besprochen wurden. Über den Parameter name kann festgelegt werden, welche der Hash-Funktionen verwendet werden soll:

>>> hashlib.pbkdf2_hmac("sha256", b"password", b"salt", 100000)
b'\x03\x94\xa2\xed\xe32\xc9\xa1>\xb8.\x9b$c\x16\x04\xc3\x1d\xf9x\xb4\xe2\xf0\xfb\xd2\xc5I\x94O\x9dy\xa5'

 


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Python 3 Python 3
Jetzt Buch bestellen

 Buchempfehlungen
Zum Rheinwerk-Shop: Einstieg in Python
Einstieg in Python


Zum Rheinwerk-Shop: Python. Der Grundkurs
Python. Der Grundkurs


Zum Rheinwerk-Shop: Algorithmen mit Python
Algorithmen mit Python


Zum Rheinwerk-Shop: Objektorientierte Programmierung
Objektorientierte Programmierung


Zum Rheinwerk-Shop: Raspberry Pi. Das umfassende Handbuch
Raspberry Pi. Das umfassende Handbuch


Zum Rheinwerk-Shop: Roboter-Autos mit dem Raspberry Pi
Roboter-Autos mit dem Raspberry Pi


Zum Rheinwerk-Shop: Neuronale Netze programmieren mit Python
Neuronale Netze programmieren mit Python


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2020
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 
[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern