security: 9 Findings aus Security-Audit behoben (CRITICAL + HIGH + MEDIUM)
CRITICAL: - C-1: LDAP tls_verify Default False → True (MITM-Schutz) - C-2: TOTP-Secret Fernet-verschlüsselt in DB (statt Plaintext) - core/crypto.py: encrypt_value() / decrypt_value() helper - Migration 0026: totp_secret VARCHAR(64→500), ldap tls_verify default=true - _totp_plain() helper mit Legacy-Fallback für bestehende Werte HIGH: - H-1: Kiosk Nonce-Cache asyncio.Lock (Race Condition behoben) - H-2: File-Upload-Limit 10 MB (import_kimai.py + users.py CSV-Import) - H-3: CORS allow_methods/allow_headers explizit eingeschränkt (war *) - H-4: TrustedHostMiddleware aktiviert wenn ALLOWED_HOSTS gesetzt MEDIUM: - M-1: IP-Logging nutzt X-Forwarded-For hinter nginx-Proxy - M-4: Audit-Log für password_changed, totp_enabled, totp_disabled - M-5: CalDAV verify_ssl in Production erzwungen (_effective_verify_ssl) 152/152 Tests grün Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
"""
|
||||
Zentrale Krypto-Hilfsfunktionen für TimeMaster.
|
||||
|
||||
Verwendet Fernet-Verschlüsselung (AES-128-CBC + HMAC-SHA256).
|
||||
Der Schlüssel wird aus SECRET_KEY per SHA-256 abgeleitet.
|
||||
|
||||
Verwendung:
|
||||
from app.core.crypto import encrypt_value, decrypt_value
|
||||
|
||||
stored = encrypt_value("geheimes-passwort")
|
||||
plain = decrypt_value(stored)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
|
||||
from cryptography.fernet import Fernet, InvalidToken
|
||||
|
||||
from app.core.config import settings
|
||||
|
||||
|
||||
def _fernet() -> Fernet:
|
||||
"""Erstellt eine Fernet-Instanz aus dem konfigurierten SECRET_KEY."""
|
||||
key = hashlib.sha256(settings.secret_key.encode()).digest()
|
||||
return Fernet(base64.urlsafe_b64encode(key))
|
||||
|
||||
|
||||
def encrypt_value(plain: str) -> str:
|
||||
"""Verschlüsselt einen Klartext-String per Fernet. Gibt den chiffrierten String zurück."""
|
||||
return _fernet().encrypt(plain.encode()).decode()
|
||||
|
||||
|
||||
def decrypt_value(encrypted: str) -> str:
|
||||
"""
|
||||
Entschlüsselt einen Fernet-verschlüsselten String.
|
||||
Wirft ValueError bei ungültigem Token oder falschem Schlüssel.
|
||||
"""
|
||||
try:
|
||||
return _fernet().decrypt(encrypted.encode()).decode()
|
||||
except InvalidToken as exc:
|
||||
raise ValueError("Entschlüsselung fehlgeschlagen – ungültiger Token oder falscher Schlüssel.") from exc
|
||||
Reference in New Issue
Block a user