Security model
This page explains how Ticketra's security mechanisms work. For the operational checklist, see the Hardening guide.
Authentication & sessions
- Login issues a JWT stored in an httpOnly, Secure, SameSite=Strict cookie — JavaScript can't read it, which neutralizes token theft via XSS.
- Tokens are verified with the algorithm pinned to
HS256, closing algorithm-confusion attacks. - Every request re-validates the session against the database: it must be un-revoked, unexpired, the user active, and (for tenant users) the tenant active. Disabling a user or revoking sessions takes effect immediately.
- Failed logins drive tiered account lockout plus per-account and per-IP rate limiting.
CSRF protection
Because authentication uses cookies, mutating requests are protected with a double-submit token: a non-httpOnly csrf_token cookie must match an X-CSRF-Token header, compared in constant time. Bearer-token (non-cookie) requests and the webhook receiver (Basic Auth, server-to-server) are exempt because they aren't subject to CSRF.
Tenant isolation
Tenant scope is resolved server-side and applied to every query. Tenant users are pinned to their own tenant and cannot escape it; system users select a tenant explicitly (header for REST, query param for SSE). See Multi-tenancy.
Input handling
- All SQL is parameterized — no ORM, no string-built queries with user input.
- File downloads (attachments, avatars, report logos) are defended against path traversal by stripping to a basename and verifying the resolved path stays inside the allowed directory.
- LDAP search filters are RFC-4515 escaped before substitution.
Secrets & encryption
- Secrets are read from Docker secret files, never hardcoded.
- CTI provider API keys are encrypted at rest with AES-256-GCM, using a key derived via scrypt from the
integration_encryption_keysecret.
Transport & headers
The frontend Nginx terminates TLS and applies a strong Content-Security-Policy (no inline scripts), HSTS, X-Frame-Options: DENY, a restrictive Permissions-Policy, and hides its version. The API additionally sets Helmet's cross-origin isolation headers and a generic 5xx error body so server internals never leak.
Defense in depth, not magic
No single control is load-bearing. An attacker who slips past one layer (say, finds an XSS vector) still faces httpOnly cookies, CSP, CSRF, tenant scoping, and server-side permission checks. That layering is the point.
When LDAP is enabled, logins for non-existent users can take longer than for known users (the unknown-user path attempts a directory lookup). Treat this as a username-enumeration consideration: keep the LDAP connect timeout low and rely on the rate limiting and lockout above. This is called out in the Hardening checklist.
Related
- Hardening guide — what to do before going live.
- Audit log & event bus