ZefiraZEFIRA Docs GitHub ↗

Security

How Zefira defends itself — and how to keep it that way.

Built-in defenses

LayerImplementation
Passwordsscrypt hashing, 10+ chars with letters + digits enforced on change
SessionsJWT in HttpOnly + SameSite=Strict cookies, Secure behind HTTPS, versioned (logout / password change kills all sessions)
2FATOTP (SHA1/6-digit/30s), secrets Fernet-encrypted at rest
Brute force8 tries / 15 min per IP+user, plus a loose global per-user bucket (lockout-DoS resistant); identical 401s + dummy-hash timing for unknown users
CSRFMutating /api calls require X-Requested-With: XMLHttpRequest
HeadersCSP, frame-ancestors 'none', X-Frame-Options DENY, nosniff, no-referrer, CORP same-origin, HSTS on HTTPS, no Server banner
InputStrict Pydantic schemas everywhere, 1 MiB body cap (64 MiB only for restore), LIKE-escape on search
SecretsBot tokens, tunnel tokens, TOTP and REALITY private keys encrypted with a host-local master key
VisibilityAudit log (last 2000 events), Telegram brute-force alerts (spam-throttled)

Penetration-test suite

security_test.py attacks a live panel: auth boundary, JWT forgery (alg=none, tampering, bad signature), rate limits, CSRF, mass assignment, backup/restore atomicity, fuzzing, XSS sources, header checks — currently 76/76 passing:

.venv\Scripts\python security_test.py http://127.0.0.1:8000 admin YOUR_PASSWORD
Restore/atomicity tests wipe users. The suite refuses to run against a panel with users unless you pass --allow-live — back up first.

Operator checklist