Per-username lockout (5 failed TOTP attempts / 5 min) stops account-targeted brute force regardless of source IP. Player.totp_secret is now Fernet- encrypted (ENCRYPTION_KEY env, db/crypto.py) instead of stored in plaintext, and auth_token is stored as a SHA-256 hash rather than the raw session token. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
43 lines
1.4 KiB
Python
43 lines
1.4 KiB
Python
"""Sifrovanie/hashovanie citlivych stlpcov (Player.totp_secret, Player.auth_token).
|
|
|
|
totp_secret sa musi dat spatne desifrovat (treba ho na vygenerovanie/overenie
|
|
TOTP kodu), preto Fernet -- symetricke sifrovanie s klucom z env ENCRYPTION_KEY.
|
|
auth_token sa iba porovnava, nikdy nepotrebujeme povodnu hodnotu spat, preto
|
|
staci jednosmerny SHA-256 hash (token ma 384 bitov entropie z
|
|
secrets.token_urlsafe(48) v api/auth.py, takze netreba salt/pepper).
|
|
"""
|
|
|
|
import hashlib
|
|
import os
|
|
|
|
from cryptography.fernet import Fernet, InvalidToken
|
|
|
|
_ENV_VAR = "ENCRYPTION_KEY"
|
|
|
|
|
|
def _fernet() -> Fernet:
|
|
key = os.environ.get(_ENV_VAR)
|
|
if not key:
|
|
raise RuntimeError(
|
|
f"{_ENV_VAR} nie je nastaveny. Vygeneruj ho pomocou:\n"
|
|
' python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"\n'
|
|
"a nastav ako env premennu (drz ho stabilny -- zmena znamena, "
|
|
"ze existujuce totp_secret sa uz nedaju desifrovat)."
|
|
)
|
|
return Fernet(key.encode())
|
|
|
|
|
|
def encrypt(plaintext: str) -> str:
|
|
return _fernet().encrypt(plaintext.encode()).decode()
|
|
|
|
|
|
def decrypt(ciphertext: str) -> str:
|
|
try:
|
|
return _fernet().decrypt(ciphertext.encode()).decode()
|
|
except InvalidToken as exc:
|
|
raise ValueError("Neplatny alebo poskodeny sifrovany udaj.") from exc
|
|
|
|
|
|
def hash_token(token: str) -> str:
|
|
return hashlib.sha256(token.encode()).hexdigest()
|