Случайность, которой можно доверять: secrets vs random
Криптография держится на случайных ключах и токенах. Но не всякая случайность одинаково полезна: модуль random в Python для секретов категорически не подходит.
Две разные случайности
Модуль random создан для игр и симуляций. Он быстрый, но предсказуемый: зная начальное состояние (seed), можно вычислить все «случайные» числа наперёд. Для метания кубика это нормально, для генерации пароля — катастрофа.
Модуль secrets создан специально для криптографии. Он берёт случайность у операционной системы и непредсказуем.
Демо: random предсказуем
import random
# Если злоумышленник угадал seed — он знает ВСЕ числа
random.seed(42)
tokens_a = [random.randint(1000, 9999) for _ in range(5)]
random.seed(42) # тот же seed
tokens_b = [random.randint(1000, 9999) for _ in range(5)]
print("Первый запуск: ", tokens_a)
print("Второй запуск: ", tokens_b)
print("Совпадают полностью:", tokens_a == tokens_b)
print("=> random ВОСПРОИЗВОДИМ, для секретов опасен")
Если сервер генерирует токены сброса пароля через random, а атакующий вычислит seed, он предскажет чужие токены и угонит аккаунты. Такое реально случалось.
Демо: secrets для секретов
import secrets
import string
# Криптостойкий пароль
alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for _ in range(16))
print("Случайный пароль:", password)
# Случайный токен (например, ссылка сброса пароля)
token = secrets.token_urlsafe(16)
print("URL-токен: ", token)
# Случайные байты для ключа
key = secrets.token_bytes(16)
print("Ключ (hex): ", key.hex())
# Безопасный выбор: подбросить честную монетку
print("Монетка: ", secrets.choice(["орёл", "решка"]))
Правило простое
| Задача | Чем брать |
|---|---|
| Игра, симуляция, перемешать список | random |
| Пароль, ключ, токен, код подтверждения | secrets |
Запомни: если случайность защищает что-то важное — всегда secrets (или os.urandom). Никогда random.
Вывод:
random предсказуем: зная seed, можно вычислить все его числа — для секретов это дыра. secrets берёт непредсказуемую случайность у ОС. Пароли, токены и ключи — только через secrets.
Проверьте себя
1. Почему модуль random нельзя использовать для генерации паролей и токенов?
AОн слишком медленный
BОн предсказуем: зная seed, можно вычислить все его «случайные» числа
CОн умеет работать только с буквами
DОн требует подключения к интернету
2. Чем правильно генерировать токен ссылки для сброса пароля?
Arandom.randint
Bsecrets.token_urlsafe
Cdatetime.now
Dпросто числом 12345