Zpět na domů

Generování hesel Python: secrets místo random

Článek rozebírá, proč modul random není vhodný pro generování hesel kvůli determinismu Mersenne Twister. Srovnání se secrets, analýza entropie a neúspěšný pokus o útok na seed. Doporučení pro bezpečný kód.

Proč je random nebezpečný pro hesla v Pythonu: secrets v akci
Advertisement 728x90

Kryptografická bezpečnost generování hesel v Pythonu: random vs secrets

Entropie určuje kvalitu hesla nikoli jako vlastnost řetězce, ale jako charakteristiku procesu generování. Vzorec H = L × log₂(N) předpokládá nezávislý a rovnoměrný výběr každého znaku z abecedy o velikosti N na délce L. Pro 8 malých písmen (N=26): ~37,6 bitů. Pro plný ASCII (N=94): ~79 bitů.

Omyl: vnější náhodnost hesla nezaručuje odolnost. Dvě hesla jako f9A$kL2pQzX1 mohou mít stejnou formální entropii, ale rozdílnou reálnou bezpečnost v závislosti na generátoru.

Modul random: deterministický PRNG

Modul random implementuje Mersenne Twister (MT19937) s vnitřním stavem ~19937 bitů. Každá hodnota je determinována předchozí. Seed (počáteční hodnota) se standardně bere z os.urandom(), ale vlastnosti PRNG zůstávají:

Google AdInline article slot
  • Determinismus: jeden seed — jedna sekvence.
  • Obnovitelnost: stav lze rekonstruovat z ~624 surových 32bitových výstupů.
  • Absence kryptografické odolnosti: není odolný vůči útokům na stav.

Příklad typického kódu:

import random
import string

alphabet = string.ascii_letters + string.digits
def generate_password(length=12):
    return ''.join(random.choice(alphabet) for _ in range(length))

Heslo je výřez sekvence PRNG. Pokud je stav znám, sekvence je předvídatelná.

Modul secrets: přímý přístup k systémové entropii

Secrets používá os.urandom() přímo pro každé volání:

Google AdInline article slot
  • secrets.randbelow(n) → os.urandom().
  • random.SystemRandom() → os.urandom().

Žádný pevný stav, žádný determinismus. Každý bajt je čerpán z hardwarových zdrojů: přerušení, síťový šum, pohyby.

Rozdíl je zásadní: random vytváří iluzi odolnosti, secrets ji zajišťuje.

Praktický útok: obnovení seed a stavu

Experiment s 5 po sobě jdoucími hesly od GigaChat (délka 12, Python 3.9.18, čas 24.03.2026 11:11):

Google AdInline article slot

Hypotéza: seed = časové razítko serveru ±1 hodina.

from datetime import datetime, timedelta

base_time = datetime(2026, 3, 24, 11, 11, 0)
window_seconds = 3600
for offset in range(-window_seconds, window_seconds):
    t = base_time + timedelta(seconds=offset)
    seed = int(t.timestamp())
    # Inicializace random.seed(seed) a kontrola

Výsledek: žádné shody. os.urandom() pro seed znemožňuje brute-force podle času.

Reálný útok na MT19937 vyžaduje 624 surových hodnot. Hesla — zkreslený výstup (choice → znak), celkem 60 výběrů za 5 hesel. Obnovení není možné.

Hranice útoku:

  • Vyžaduje surové výstupy PRNG.
  • Předvídatelný seed (čas, PID).
  • Opakované použití jednoho generátoru.
  • Známý algoritmus.

Co je důležité

  • Entropie — vlastnost procesu, ne výsledku: kontrolujte generátor.
  • random (Mersenne Twister) je zranitelný vůči obnovení stavu při dostatečných datech.
  • secrets/os.urandom — jediná volba pro hesla, klíče, tokeny.
  • Iluze náhodnosti od random je nebezpečná v produkci.
  • Útoky na PRNG jsou reálné pouze při chybách v seed nebo úniku stavů.

Doporučení pro middle/senior vývojáře

Implementujte do kódu:

import secrets
import string

alphabet = string.ascii_letters + string.digits + string.punctuation
def secure_password(length=16):
    return ''.join(secrets.choice(alphabet) for _ in range(length))

Vyhýbejte se:

  • random pro autentizaci.
  • Pevným seed.
  • Znovupoužívání generátoru.

Testujte: generujte 10⁶ hesel, kontrolujte rovnoměrné rozdělení (chi²).

— Editorial Team

Advertisement 728x90

Číst dál