Zum Inhalt springen
aviral gupta

// A5.5 · ca. 45 Min. · Vertiefung

Secrets, Hashing und Veröffentlichen

Nach dieser Lektion erzeugen Sie Tokens mit secrets, hashen und signieren Daten mit hashlib und hmac, speichern Passwörter mit einem langsamen Hash mit Salt, prüfen eine pyproject.toml vor python3 -m build und schließen das Modul mit einem sicheren t-String-Berichtsgenerator ab.

Lektion 5 von 5 in A5 Modernes Python und sicherer Code

Ende des Moduls

Danach können Sie

  • Tokens mit secrets und Prüfsummen mit hashlib erzeugen
  • Daten mit hmac und compare_digest signieren und prüfen und Passwörter mit Salt hashen
  • Einen sicheren t-String-Berichtsgenerator bauen und ein Paket für build und twine vorbereiten
  1. Aufwärmen · Aufgabe 1 von 7

    Aufwärmen aus der vorigen Lektion: json kann nur einfache Daten bauen. Was gibt das aus?

    import json
    
    print(json.loads('[1, 2]') == [1, 2], type(json.loads('true')).__name__)
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie es vorher, bevor Sie weiterlesen: Ein Byte und 100.000 Bytes gehen durch SHA-256. Was gibt das aus?

    import hashlib
    
    short = hashlib.sha256(b"a").hexdigest()
    long = hashlib.sha256(b"a" * 100_000).hexdigest()
    print(len(short), len(long))
  3. Üben · Aufgabe 3 von 7

    Setzen Sie die secrets-Funktion ein, die 16 Zufallsbytes als URL-sicheren Text liefert.

    import secrets
    import string
    
    token = secrets.____(16)
    allowed = set(string.ascii_letters + string.digits + "-_")
    print(len(token), set(token) <= allowed)
    token = secrets.(16)
  4. Üben · Aufgabe 4 von 7

    Ein Server signiert "user=ada". Was gibt das aus?

    import hmac
    
    key = b"server-key"
    tag = hmac.new(key, b"user=ada", "sha256").hexdigest()
    again = hmac.new(key, b"user=ada", "sha256").hexdigest()
    forged = hmac.new(key, b"user=admin", "sha256").hexdigest()
    print(hmac.compare_digest(tag, again), hmac.compare_digest(tag, forged))
  5. Üben · Aufgabe 5 von 7

    Ordnen Sie jeder Aufgabe das Werkzeug zu, das dafür gemacht ist.

  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. compare_digest ist streng bei seinen Argumenten. Was gibt das aus?

    import hmac
    
    for a, b in [("abc", "abc"), (b"abc", b"abc"), ("abc", b"abc")]:
        try:
            print(hmac.compare_digest(a, b), end=" ")
        except TypeError:
            print("TypeError", end=" ")
  7. Anwenden · Aufgabe 7 von 7

    Kleine Aufgabe. Schreiben Sie sign_cookie(value), das value + "|" + sein HMAC-SHA256-Hex-Tag unter einem Serverschlüssel liefert, und read_cookie(cookie), das den Wert liefert, wenn das Tag passt, sonst None. Vergleichen Sie mit hmac.compare_digest. Probieren Sie ein echtes Cookie, eines, in dem ada zu admin geändert wurde, und eines mit erfundenem Tag.

    Prüfen Sie Ihr Ergebnis anhand dieser Liste

Selbst programmieren

Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.

Ausgearbeitetes Beispiel

Ein Token, eine Prüfsumme und eine Signatur

Das Programm erzeugt ein Token mit secrets und gibt nur seine Länge aus, denn der Wert ändert sich bei jedem Lauf. Es hasht einen kleinen CSV-Bericht mit SHA-256, signiert ihn dann mit hmac und prüft zwei Kopien: eine unverändert und eine mit einer zusätzlichen Zeile. Nur die unveränderte Kopie passt zum Tag.

main.py

import hashlib
import hmac
import secrets

# 1. A token for a reset link: secrets, never random.
token = secrets.token_urlsafe(32)
print("token length:", len(token))

# 2. A checksum: the same bytes always give the same SHA-256 digest.
data = b"report,2026\nada,3\n"
print("sha256:", hashlib.sha256(data).hexdigest()[:16])

# 3. A signature: only someone with the key can make a matching tag.
key = b"keep-this-secret"
tag = hmac.new(key, data, "sha256").hexdigest()
received = hmac.new(key, data, "sha256").hexdigest()
tampered = hmac.new(key, data + b"bob,9\n", "sha256").hexdigest()
print("genuine:", hmac.compare_digest(tag, received))
print("tampered:", hmac.compare_digest(tag, tampered))

Ausführen mit

python main.py

Ausgabe

token length: 43
sha256: 24ba945e8f82a042
genuine: True
tampered: False
  • token_urlsafe(32) ergibt 43 Zeichen: 32 Bytes in URL-sicherem Base64, ohne Auffüllung.
  • Die SHA-256-Zeile ist bei jedem Lauf und auf jedem Rechner gleich.
  • Ohne den Schlüssel kann niemand ein Tag erzeugen, das zum veränderten Bericht passt.
Ändern und ausführen

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Übungen

Übung 1 von 4

Abschluss Schritt 1: Tokens und Signaturen

Reparieren Sie drei Funktionen. make_token nutzt random und nur 8 Bytes; liefern Sie secrets.token_urlsafe(32). sign hasht key + text, und das ist kein HMAC; liefern Sie das Hex-HMAC-SHA256-Tag von text.encode() unter key. verify vergleicht mit ==; nutzen Sie hmac.compare_digest.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Importieren Sie secrets und hmac; random und hashlib brauchen Sie nicht mehr.

  2. Hinweis 2

    hmac.new(key, text.encode(), "sha256").hexdigest() ist das Tag.

  3. Hinweis 3

    verify liefert hmac.compare_digest(sign(key, text), tag).

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

import hmac
import secrets


def make_token() -> str:
    """Return a URL-safe token with 32 bytes of randomness."""
    return secrets.token_urlsafe(32)


def sign(key: bytes, text: str) -> str:
    """Return the hex HMAC-SHA256 tag of text under key."""
    return hmac.new(key, text.encode(), "sha256").hexdigest()


def verify(key: bytes, text: str, tag: str) -> bool:
    """Return True if tag is the right tag for text under key."""
    return hmac.compare_digest(sign(key, text), tag)
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

import hashlib
import random


def make_token() -> str:
    """Return a URL-safe token with 32 bytes of randomness."""
    return "%016x" % random.getrandbits(64)


def sign(key: bytes, text: str) -> str:
    """Return the hex HMAC-SHA256 tag of text under key."""
    return hashlib.sha256(key + text.encode()).hexdigest()


def verify(key: bytes, text: str, tag: str) -> bool:
    """Return True if tag is the right tag for text under key."""
    return sign(key, text) == tag

test_main.py

import string

from main import make_token, sign, verify

URLSAFE = set(string.ascii_letters + string.digits + "-_")


def test_token_shape():
    """Ein Token hat 43 URL-sichere Zeichen (32 Zufallsbytes)"""
    token = make_token()
    assert len(token) == 43 and set(token) <= URLSAFE, f"make_token lieferte {token!r}; nutzen Sie secrets.token_urlsafe(32)"


def test_tokens_differ():
    """Zwei Tokens sind verschieden"""
    assert make_token() != make_token(), "make_token lieferte zweimal dasselbe Token"


def test_sign_is_hmac():
    """sign liefert das HMAC-SHA256-Tag"""
    got = sign(b"key", "msg")
    want = "2d93cbc1be167bcb1637a4a23cbff01a7878f0c50ee833954ea5221bb1b8c628"
    assert got == want, f"sign(b'key', 'msg') lieferte {got!r}; nutzen Sie hmac.new(key, text.encode(), 'sha256')"


def test_verify():
    """verify nimmt das richtige Tag an und lehnt geänderten Text oder einen anderen Schlüssel ab"""
    tag = sign(b"key", "user=ada")
    assert verify(b"key", "user=ada", tag), "das richtige Tag wurde abgelehnt"
    assert not verify(b"key", "user=admin", tag), "ein Tag für anderen Text wurde angenommen"
    assert not verify(b"other", "user=ada", tag), "ein Tag mit einem anderen Schlüssel wurde angenommen"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Übung 2 von 4

Abschluss Schritt 2: Passwörter speichern

hash_password speichert ein einfaches SHA-256 des Passworts: ohne Salt und schnell zu erraten. Speichern Sie stattdessen "salt$hash": 16 Zufallsbytes aus secrets als Salt und hashlib.pbkdf2_hmac("sha256", password.encode(), salt, ITERATIONS) als Hash, beides hexkodiert. check_password rechnet mit dem gespeicherten Salt neu und vergleicht mit hmac.compare_digest. Führen Sie die Tests auf Ihrem eigenen Rechner aus: Der Browser hat kein pbkdf2_hmac.

Diese Übung braucht Python auf Ihrem Computer (die Browser-Version kann sie nicht ausführen). Dateien und Befehle stehen unten.

Hinweise
  1. Hinweis 1

    salt = secrets.token_bytes(16), dann return salt.hex() + "$" + digest.hex().

  2. Hinweis 2

    In check_password trennen Sie an "$" und machen das Salt mit bytes.fromhex wieder zu bytes.

  3. Hinweis 3

    Vergleichen Sie digest.hex() mit dem gespeicherten Hex-Wert per hmac.compare_digest.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

import hashlib
import hmac
import secrets

ITERATIONS = 100_000


def hash_password(password: str) -> str:
    """Return "salt$hash" in hex, with a new random salt each time."""
    salt = secrets.token_bytes(16)
    digest = hashlib.pbkdf2_hmac("sha256", password.encode(), salt, ITERATIONS)
    return salt.hex() + "$" + digest.hex()


def check_password(password: str, stored: str) -> bool:
    """Return True if password matches what hash_password stored."""
    salt_hex, digest_hex = stored.split("$")
    digest = hashlib.pbkdf2_hmac("sha256", password.encode(), bytes.fromhex(salt_hex), ITERATIONS)
    return hmac.compare_digest(digest.hex(), digest_hex)
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

import hashlib

ITERATIONS = 100_000


def hash_password(password: str) -> str:
    """Return "salt$hash" in hex, with a new random salt each time."""
    # sha256 of the bare password: no salt, and fast to brute-force.
    return "$" + hashlib.sha256(password.encode()).hexdigest()


def check_password(password: str, stored: str) -> bool:
    """Return True if password matches what hash_password stored."""
    return hash_password(password) == stored

test_main.py

import hashlib

from main import check_password, hash_password


def test_round_trip():
    """Das richtige Passwort passt, ein falsches nicht"""
    stored = hash_password("hunter2")
    assert check_password("hunter2", stored), "das richtige Passwort wurde abgelehnt"
    assert not check_password("hunter3", stored), "ein falsches Passwort wurde angenommen"


def test_salted():
    """Dasselbe Passwort zweimal gehasht ergibt verschiedene Ergebnisse"""
    assert hash_password("hunter2") != hash_password("hunter2"), "kein zufälliges Salt: gleiche Passwörter ergeben gleiche Hashes"


def test_uses_pbkdf2():
    """Der gespeicherte Hash ist PBKDF2-HMAC-SHA256 mit einem 16-Byte-Salt"""
    salt_hex, _, digest_hex = hash_password("pw").partition("$")
    assert len(salt_hex) == 32, f"der Salt-Teil ist {salt_hex!r}; nutzen Sie 16 Zufallsbytes, hexkodiert"
    want = hashlib.pbkdf2_hmac("sha256", b"pw", bytes.fromhex(salt_hex), 100_000).hex()
    assert digest_hex == want, "der Hash-Teil ist nicht pbkdf2_hmac('sha256', password, salt, ITERATIONS)"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Übung 3 von 4

Abschluss Schritt 3: ein sicherer Bericht mit t-Strings

render(template) setzt Werte roh ein, und report baut seine Zeilen mit f-Strings, ein Name wie "<b>bob</b>" wird also HTML. Lassen Sie render die Umwandlung und Formatangabe jeder Interpolation anwenden und das Ergebnis dann mit html.escape maskieren. Bauen Sie danach die Zeilen von report als t-Strings und geben Sie sie durch render.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    from string.templatelib import convert: value = convert(item.value, item.conversion).

  2. Hinweis 2

    Dann escape(format(value, item.format_spec)), mit escape aus html.

  3. Hinweis 3

    Schreiben Sie in report render(t"<li>{name}: {score:>3}</li>"): Maskiert werden nur die Werte, nicht Ihre eigenen Tags.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

from html import escape
from string.templatelib import Interpolation, Template, convert


def render(template: Template) -> str:
    """Join the template, HTML-escaping every interpolated value."""
    parts: list[str] = []
    for item in template:
        if isinstance(item, Interpolation):
            value = convert(item.value, item.conversion)
            parts.append(escape(format(value, item.format_spec)))
        else:
            parts.append(item)
    return "".join(parts)


def report(title: str, rows: list[tuple[str, int]]) -> str:
    """Return an HTML report: a heading and one list item per row."""
    lines = [render(t"<h1>{title}</h1>")]
    for name, score in rows:
        lines.append(render(t"<li>{name}: {score:>3}</li>"))
    return "\n".join(lines)
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

from string.templatelib import Interpolation, Template


def render(template: Template) -> str:
    """Join the template, HTML-escaping every interpolated value."""
    return "".join(
        str(item.value) if isinstance(item, Interpolation) else item for item in template
    )


def report(title: str, rows: list[tuple[str, int]]) -> str:
    """Return an HTML report: a heading and one list item per row."""
    lines = [f"<h1>{title}</h1>"]
    for name, score in rows:
        lines.append(f"<li>{name}: {score:>3}</li>")
    return "\n".join(lines)

test_main.py

from main import render, report


def test_render_escapes():
    """Eingesetzte Werte werden HTML-maskiert"""
    x = "<script>"
    got = render(t"<p>{x}</p>")
    assert got == "<p>&lt;script&gt;</p>", f"render lieferte {got!r}"


def test_render_format_spec():
    """Die Formatangabe wird angewandt"""
    n = 7
    got = render(t"[{n:>4}]")
    assert got == "[   7]", f"render lieferte {got!r}; wenden Sie format(value, item.format_spec) an"


def test_render_conversion():
    """!r wird angewandt, danach maskiert"""
    v = "a<b"
    got = render(t"{v!r}")
    assert got == "&#x27;a&lt;b&#x27;", f"render lieferte {got!r}; wenden Sie convert(value, item.conversion) an"


def test_report():
    """Der Bericht maskiert den Titel und jeden Namen"""
    got = report("Scores <2026>", [("ada", 3), ("<b>bob</b>", 12)])
    want = "<h1>Scores &lt;2026&gt;</h1>\n<li>ada:   3</li>\n<li>&lt;b&gt;bob&lt;/b&gt;:  12</li>"
    assert got == want, f"report lieferte {got!r}"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Übung 4 von 4

Abschluss Schritt 4: pyproject.toml vor dem Build prüfen

Prüfen Sie vor python3 -m build die Projektdatei. package_info(text) soll "name version" liefern und ValueError auslösen, außer es gibt eine Tabelle [build-system] mit einer Liste requires und einem String build-backend sowie eine Tabelle [project], deren name und version Strings sind. Der Starter stürzt stattdessen mit KeyError oder TypeError ab.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Nutzen Sie data.get("project") und data.get("build-system") und prüfen Sie, ob beides ein dict ist.

  2. Hinweis 2

    Prüfen Sie isinstance(name, str) und isinstance(version, str); version = 1 ist in TOML ein int.

  3. Hinweis 3

    Prüfen Sie isinstance(system.get("requires"), list) und isinstance(system.get("build-backend"), str).

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

import tomllib


def package_info(text: str) -> str:
    """Check a pyproject.toml before building; return "name version"."""
    data = tomllib.loads(text)
    project = data.get("project")
    system = data.get("build-system")
    if not isinstance(project, dict) or not isinstance(system, dict):
        raise ValueError("need a [project] and a [build-system] table")
    name = project.get("name")
    version = project.get("version")
    if not isinstance(name, str) or not isinstance(version, str):
        raise ValueError("[project] needs name and version as strings")
    if not isinstance(system.get("requires"), list) or not isinstance(system.get("build-backend"), str):
        raise ValueError("[build-system] needs a requires list and a build-backend string")
    return f"{name} {version}"
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

import tomllib


def package_info(text: str) -> str:
    """Check a pyproject.toml before building; return "name version"."""
    data = tomllib.loads(text)
    return data["project"]["name"] + " " + data["project"]["version"]

test_main.py

from main import package_info

GOOD = """
[build-system]
requires = ["hatchling >= 1.26"]
build-backend = "hatchling.build"

[project]
name = "example_package"
version = "0.0.1"
"""

NO_BUILD_SYSTEM = """
[project]
name = "example_package"
version = "0.0.1"
"""

NUMBER_VERSION = """
[build-system]
requires = ["hatchling >= 1.26"]
build-backend = "hatchling.build"

[project]
name = "example_package"
version = 1
"""

NO_PROJECT = """
[build-system]
requires = ["hatchling >= 1.26"]
build-backend = "hatchling.build"
"""


def rejects(text):
    try:
        package_info(text)
    except ValueError:
        return True
    return False


def test_good():
    """Eine vollständige pyproject.toml ergibt Name und Version"""
    got = package_info(GOOD)
    assert got == "example_package 0.0.1", f"package_info lieferte {got!r}"


def test_needs_build_system():
    """Eine fehlende Tabelle [build-system] wird abgelehnt"""
    assert rejects(NO_BUILD_SYSTEM), "eine Datei ohne [build-system] wurde angenommen oder löste den falschen Fehler aus"


def test_version_is_string():
    """Eine Version als Zahl wird abgelehnt"""
    assert rejects(NUMBER_VERSION), "version = 1 wurde angenommen oder löste den falschen Fehler aus; sie muss ein String sein"


def test_needs_project():
    """Eine fehlende Tabelle [project] wird mit ValueError abgelehnt"""
    assert rejects(NO_PROJECT), "eine Datei ohne [project] wurde angenommen oder löste den falschen Fehler aus"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Häufige Fehler

Einen str hashen

import hashlib

digest = hashlib.sha256("report,2026").hexdigest()

Was Python ausgibt

TypeError: Strings must be encoded before hashing

Warum, und die Lösung

Hashfunktionen arbeiten mit bytes, und derselbe Text lässt sich unterschiedlich kodieren. Kodieren Sie ausdrücklich, meist "report,2026".encode() (UTF-8), damit jedes Programm, das den Digest prüft, dieselben Bytes hasht.

hmac.new ohne Hash-Algorithmus

import hmac

tag = hmac.new(b"server-key", b"user=ada").hexdigest()

Was Python ausgibt

TypeError: Missing required argument 'digestmod'.

Warum, und die Lösung

digestmod ist Pflicht: Übergeben Sie den Namen des Algorithmus als drittes Argument, hmac.new(key, msg, "sha256"), oder als digestmod=hashlib.sha256. Es gibt keinen Standardwert, die Wahl ist also immer im Code sichtbar.

Ein str-Passwort für pbkdf2_hmac

import hashlib
import secrets

salt = secrets.token_bytes(16)
key = hashlib.pbkdf2_hmac("sha256", "hunter2", salt, 100_000)

Was Python ausgibt

TypeError: a bytes-like object is required, not 'str'

Warum, und die Lösung

pbkdf2_hmac liest Passwort und Salt als bytes. Kodieren Sie das Passwort zuerst: "hunter2".encode(). Das Salt aus secrets.token_bytes ist schon bytes.

Python im Browser: Pyodide 314.0.7, MPL-2.0. Lizenz und Quellcode

Abschlussquiz

5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.

Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.

Problem melden

Etwas ist falsch oder unklar? Beschreiben Sie es kurz, dann wird es geprüft und korrigiert.

#

Mindestens 20 Zeichen.

Nur, wenn Sie eine Antwort wünschen.

Kernideen

Tokens aus secrets, Prüfsummen aus hashlib

Das Modul random ist für Simulationen gebaut; die Doku empfiehlt für alles Sicherheitsrelevante secrets. secrets.token_urlsafe(n) liefert n Zufallsbytes als URL-sicheren Text (etwa 1,3 Zeichen pro Byte), token_hex(n) liefert 2n Hexziffern, und ohne Argument nutzen beide einen Standardwert, der sich laut Doku ändern kann; 32 Bytes sind ihre aktuelle Empfehlung. hashlib.sha256(data) nimmt bytes, Text müssen Sie also erst kodieren. Der Digest hat eine feste Größe, 32 Bytes oder 64 Hexziffern, egal wie lang die Eingabe ist, und dieselben Bytes ergeben immer denselben Digest: gut für Prüfsummen, aber jeder kann ihn berechnen, er beweist also nicht, wer die Daten erstellt hat.

Signaturen mit hmac; Passwörter mit langsamem Hash und Salt

hmac.new(key, msg, "sha256") mischt einen geheimen Schlüssel in den Hash, nur Schlüsselinhaber können also ein passendes Tag erzeugen; digestmod ist Pflicht. Vergleichen Sie Tags mit hmac.compare_digest, nie mit ==: Es bricht nicht je nach Inhalt früh ab, die Laufzeit verrät also nicht, wie viel eines geratenen Tags stimmte. Es nimmt zwei str (nur ASCII) oder zwei bytes. Für Passwörter ist ein einfacher Hash zu schnell, um Rateversuchen standzuhalten. hashlib.pbkdf2_hmac nimmt ein zufälliges Salt und eine Iterationszahl, und Sie speichern beides mit dem Ergebnis. Pyodide hat kein pbkdf2_hmac, diese Übung läuft also auf Ihrem eigenen Rechner.

Veröffentlichen mit build und twine; der Modulabschluss

Das Packaging-Tutorial beschreibt die Schritte. pyproject.toml nennt in [build-system] mit requires und build-backend ein Build-Backend und in [project] die Paket-Metadaten. python3 -m build schreibt zwei Dateien nach dist/: eine .tar.gz-Quelldistribution und eine gebaute .whl-Distribution. twine upload lädt sie hoch, zuerst zu TestPyPI, einem eigenen Index zum Testen, mit einem API-Token. Hier prüfen Sie nur pyproject.toml mit tomllib; hochgeladen wird nichts. Der Abschluss führt das Modul zusammen: ein Berichtsgenerator, dessen render-Funktion jeden Wert eines t-Strings maskiert, sodass von Nutzern eingegebene Namen kein HTML einschleusen können.

Quellen

Zuletzt geprüft am 29. September 2026