Security Architecture
The Security Architecture is based on defense-in-depth with 8 security layers: Transport Security (TLS 1.2+ via Traefik), Application Security (RBAC, JWT), Data Security (SHA-256 Audit Chain, AES-256 Encryption), Container Security (Zero-Trust, Least Privilege), File Security (ClamAV Virus Scan), API Security (SSRF Protection, Rate Limiting), Authentication (SSO, native MFA/TOTP, FIPS 140-2 compatible) and Audit & Compliance Support (ISO 27001, GDPR).
Security Layers Overview
┌────────────────────────────────────────────────────────────────────────┐ │ DEFENSE-IN-DEPTH (8 LAYERS) │ └────────────────────────────────────────────────────────────────────────┘ Layer 1: TRANSPORT SECURITY (Traefik API Gateway) ┌──────────────────────────────────────────────────────────────────────┐ │ • TLS 1.2/1.3 only (no SSLv3, TLS 1.0, TLS 1.1) │ │ • Strong Ciphers: ECDHE-RSA-AES-GCM, ECDHE-ECDSA, ChaCha20-Poly1305 │ │ • HSTS: max-age=31536000; includeSubDomains; preload │ │ • HTTP → HTTPS Redirect (Traefik entrypoint redirect) │ │ • Rate-Limiting: 100 req/s, burst 200 (Traefik middleware) │ │ • Internal service interfaces blocked externally (priority routing) │ │ • Cloudflare Turnstile CAPTCHA (Bot Protection) │ └──────────────────────────────────────────────────────────────────────┘ Layer 2: APPLICATION SECURITY ┌──────────────────────────────────────────────────────────────────────┐ │ • JWT Authentication (HttpOnly Cookies) │ │ • RBAC: 28+ Permission-Module, 350+ Individual Permissions │ │ • Permission cache per role, writes always fresh from DB │ │ • Permissions loaded per request, not from token │ │ • CSP Headers via Traefik: default-src 'self', script-src 'self' │ │ • X-Frame-Options: SAMEORIGIN │ │ • X-Content-Type-Options: nosniff │ │ • X-XSS-Protection: 1; mode=block │ │ • Referrer-Policy: strict-origin-when-cross-origin │ │ • Permissions-Policy: geolocation=(self), microphone=(), camera=() │ │ • CSRF Protection (built-in) │ └──────────────────────────────────────────────────────────────────────┘ Layer 3: DATA SECURITY ┌──────────────────────────────────────────────────────────────────────┐ │ • SHA-256 Audit-Chain (immutable, tamper-evident) │ │ • PII-Scrubbing (Passwords, Tokens auto-redacted) │ │ • AES-256-GCM Encryption (License-Keys, TOTP, alle Credentials) │ │ • Optimistic Locking (version field prevents concurrent mods) │ │ • Soft-Delete (Recovery-Window) │ └──────────────────────────────────────────────────────────────────────┘ Layer 4: CONTAINER SECURITY ┌──────────────────────────────────────────────────────────────────────┐ │ • Read-only Filesystem (av-worker) │ │ • Capability-Drop ALL (av-worker) │ │ • Capability-Drop 3 (ClamAV: NET_RAW, SYS_ADMIN, MKNOD) │ │ • Non-root Users (all workers: UID 1001) │ │ • no-new-privileges (Traefik, ClamAV, av-worker) │ │ • tmpfs for temporary storage (in-memory, volatile) │ │ • Resource-Limits (ClamAV: 2GB, av-worker: 1.5GB) │ │ • Redis Password Authentication (--requirepass) │ └──────────────────────────────────────────────────────────────────────┘ Layer 5: FILE SECURITY ┌──────────────────────────────────────────────────────────────────────┐ │ • Zero-Trust Virus-Scan (ClamAV, av-worker has NO file access) │ │ • Scan-Status-Tracking: PENDING → SCANNING → CLEAN/INFECTED │ │ • Quarantine-Volume (infected files isolated) │ │ • Extension-Blacklist (.exe, .bat, .sh, .dll, .js, .vbs) │ │ • MIME-Type Validation (server-side) │ │ • File-Size-Limits (Global: 100MB, per-Entity configurable) │ │ • Max-Files-Limit (per entity) │ │ • Retention-Policy (Auto-Delete after X days) │ │ • Orphan-Cleanup (unused files after 24h) │ └──────────────────────────────────────────────────────────────────────┘ Layer 6: API SECURITY ┌──────────────────────────────────────────────────────────────────────┐ │ • SSRF Protection (Webhooks, Workflow-Actions, Teams-URLs) │ │ • HMAC Signature (webhooks with shared secret) │ │ • Rate-Limiting (all values ENV-configurable): │ │ - Traefik Gateway: 100 req/s, burst 200 │ │ - Global /api/*: 2000/min/IP · Critical-Ops: 60/min (writes only) │ │ - Login: 5 Fails/15min je (IP+Konto) + 30/15min/IP Backstop │ │ - File Uploads: 200/hour/IP (FILE_UPLOAD_RATE_LIMIT) │ │ - Email Inbound: 60/min global, 30/hour/sender │ │ • Input Validation (Zod schemas) │ │ • SQL Injection Protection (Prisma ORM) │ │ • XSS Protection (DOMPurify client-side) │ │ • API-Keys (INTERNAL_API_KEY for worker auth) │ └──────────────────────────────────────────────────────────────────────┘ Layer 7: AUTHENTICATION & AUTHORIZATION ┌──────────────────────────────────────────────────────────────────────┐ │ • JWT Tokens (HttpOnly Cookies) │ │ • Password Hashing (PBKDF2-SHA512, 210k iterations — FIPS 140-2) │ │ • Multi-Factor Authentication (native TOTP + Entra ID MFA) │ │ • Entra ID SSO (Azure AD, OAuth 2.0) │ │ • API-Keys (for external integrations) │ │ • Token Refresh (refresh_token flow) │ │ • Session Management (Redis-backed, configurable expiry) │ │ • Cloudflare Turnstile CAPTCHA (Bot Protection) │ └──────────────────────────────────────────────────────────────────────┘ Layer 8: COMPLIANCE & AUDIT ┌──────────────────────────────────────────────────────────────────────┐ │ • SHA-256 Hash-Chain (tamper-evident, immutable) │ │ • Immutability (DB trigger prevents UPDATE/DELETE) │ │ • 9 Audit-Domains (AUTH, ENTITY, ADMIN, SECURITY, SLA, etc.) │ │ • Chain-Verification-API (Quick-Check & Full-Verification) │ │ • FIPS 140-2 compatible algorithms (Standard + FIPS Mode) │ │ • Supports ISO 27001, SOC 2 and GDPR audits │ │ • Retention-Policies (7+ years for Audit-Logs) │ └──────────────────────────────────────────────────────────────────────┘
1. Transport Security (Traefik API Gateway)
TLS/SSL Configuration
Transport security is provided by Traefik v3 as the central API gateway. Traefik terminates TLS, sets security headers, and enforces rate limits before requests reach the backend.
| Feature | Configuration | Details |
|---|---|---|
| TLS Versions | TLS 1.2, TLS 1.3 | Blocked: SSLv3, TLS 1.0, TLS 1.1 (Traefik minVersion: VersionTLS12) |
| Cipher Suites | ECDHE-RSA/ECDSA-AES-GCM, ChaCha20-Poly1305 | Perfect forward secrecy, 6 explicit cipher suites |
| HSTS | max-age=31536000; includeSubDomains; preload | 1 year, all subdomains, preload-ready |
| HTTP Redirect | 301 Permanent Redirect | Enforces HTTPS (Traefik entrypoint redirect) |
| Rate-Limiting | 100 req/s, burst 200 | DDoS protection at gateway level (Traefik middleware) |
| Internal interface block | Priority-Router → noop@internal | Internal interfaces between the services are not reachable from outside (blocked by Traefik) |
External Reverse Proxy: When running behind an external reverse proxy (e.g. Nginx, HAProxy), TRUSTED_PROXIES (.env) and forwardedHeaders.trustedIPs (traefik/traefik.yml) must be configured so client IPs are logged correctly and rate limiting applies to the real client IP. Installation → External Reverse Proxy
📘 Details: See Container Architecture → (Traefik Container)
Traefik Routing & Priorities
| Router | Rule | Priority | Middlewares |
|---|---|---|---|
| block-internal-api | PathPrefix(/api/internal) | 40 | security-headers |
| backend-api | PathPrefix(/api) | 30 | security-headers, compress, rate-limit |
| backend-ws | PathPrefix(/socket.io) | 20 | security-headers |
| frontend-spa | PathPrefix(/) | 10 | security-headers, compress, rate-limit |
2. Application Security
Role-Based Access Control (RBAC)
- 28+ Permission Modules: tickets, problems, changes, incidents, assets, contracts, licenses, workflows, cronjobs, users, settings, audit, etc.
- 200+ Individual Permissions: viewAll, viewOwn, create, editStatus, assign, approve, delete
- Permission Cache: per role, 5 min for read requests; modifying requests and critical actions always read permissions fresh from the database
- No Permissions in the Token: permissions are loaded from the role on every request; an older token grants no permissions that have since been removed
- 4 System Roles: END_USER, AGENT, ADMIN, APPROVER (cannot be deleted or deactivated)
- Unified Actor: User (cookie) and API key (X-API-Key with role) go through the same permission check against their role's matrix
📘 Details: See Permissions & RBAC →, User Management → and Users & Roles API →
Security Headers (via Traefik)
All security headers are set centrally through the Traefik security-headers middleware:
| Header | Value | Purpose |
|---|---|---|
Content-Security-Policy |
default-src 'self'; script-src 'self' https://challenges.cloudflare.com | XSS protection (Turnstile CAPTCHA allowed) |
X-Frame-Options |
SAMEORIGIN | Clickjacking protection |
X-Content-Type-Options |
nosniff | Prevent MIME sniffing |
X-XSS-Protection |
1; mode=block | Enable XSS filter |
Referrer-Policy |
strict-origin-when-cross-origin | Prevent referrer leak |
Permissions-Policy |
geolocation=(self), microphone=(), camera=(self) | Restrict browser permissions |
Strict-Transport-Security |
max-age=31536000; includeSubDomains; preload | Enforce HTTPS (1 year) |
3. Data Security
SHA-256 Audit Chain
- Immutable Logs: UPDATE/DELETE forbidden (DB trigger)
- Hash Chain: Each event links to previous via SHA-256
- Chain Verification: API endpoint validates entire chain integrity
- PII Scrubbing: Automatically redacts passwords, tokens, email, phone
- Multi-Org Isolation: Separate chains per organization
- Redis Fallback: On DB failure → Redis backup (7 days TTL)
📘 Details: See Enterprise Audit System →
Encryption at Rest
| Data Type | Encryption | Key Management |
|---|---|---|
| License keys | AES-256-GCM | LICENSE_ENCRYPTION_KEY (env) |
| Passwords | PBKDF2-SHA512 (210k iterations) | One-way hash (FIPS 140-2) |
| TOTP secrets | AES-256-GCM | TWO_FACTOR_ENCRYPTION_KEY (env) |
| Mailbox credentials (SMTP/IMAP) | AES-256-GCM | MAILBOX_ENCRYPTION_KEY (env) |
| Global credentials: SMTP password, MS-Graph & Entra ID clientSecret, notification adapters (Webex botToken, Teams appPassword) | AES-256-GCM | MAILBOX_ENCRYPTION_KEY (env) |
| JWT Tokens | HMAC-SHA256 (HS256) | JWT_SECRET (env) |
All global credentials are encrypted with AES-256-GCM on save (the same key as the mailbox credentials: MAILBOX_ENCRYPTION_KEY, falling back to LICENSE_ENCRYPTION_KEY) and decrypted only when used. If a stored value cannot be decrypted, the function stops with an error; the credentials must then be re-entered.
📘 Details: See Contracts & Licenses API → (License Key Encryption)
Optimistic Locking
- Incidents: Version field prevents concurrent modifications
- Problems: Version field for conflict detection
- Assets: Version field on updates
- Contracts: Version field on financial updates
- Conflict Response: 409 CONFLICT with current entity state
4. Container Security
Zero-Trust Architecture (av-worker)
# av-worker: MAXIMUM HARDENING
read_only: true # Filesystem fully read-only
tmpfs:
- /app/tmp:size=64M,mode=1777 # In-memory temporary storage
- /tmp:size=64M,mode=1777
security_opt:
- no-new-privileges:true # Prevents privilege escalation
cap_drop:
- ALL # All capabilities dropped!
# AV-Worker has NO access to the uploads volume
# Communicates only via:
# 1. ClamAV TCP API (Port 3310)
# 2. Backend HTTP API (Status-Updates)
🔒 Zero-Trust: The av-worker is the most hardened container. See Container Architecture → and Attachments API →
Traefik Container Security
# Traefik: API gateway hardening
security_opt:
- no-new-privileges:true # Prevents privilege escalation
volumes:
- ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro # Read-only config
- ./traefik/dynamic.yml:/etc/traefik/dynamic.yml:ro # Read-only routing
- ./certs/cert.pem:/etc/traefik/ssl/cert.pem:ro # Read-only cert
- ./certs/cert.key:/etc/traefik/ssl/cert.key:ro # Read-only key
Redis Password Authentication
Redis is password-protected. All services connect with an authenticated URL:
# Redis: password-protected
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
# All services connect via:
# redis://:${REDIS_PASSWORD}@redis:6379
Least Privilege Database Access
| DB User | Permissions | Used By |
|---|---|---|
helpdesk_user |
Full access (all tables) | Backend (Migrations) |
helpdesk_jobworker |
Restricted: CronJob, JobExecution, WorkerInstance | Job-Worker |
helpdesk_readonly |
SELECT-only (all tables) | Analytics, reporting |
📘 Details: See Container Architecture → (PostgreSQL Container)
Container Hardening Summary
| Container | Read-Only | Non-Root | Cap-Drop | no-new-priv | Resources | Hardening Level |
|---|---|---|---|---|---|---|
| AV-Worker | ✅ | ✅ | ✅ ALL | ✅ | 1.5G / 1C | ⭐⭐⭐⭐⭐ |
| Email-Worker | ✅ | ✅ | ✅ ALL | ✅ | 512M / 1C | ⭐⭐⭐⭐⭐ |
| Job-Worker | ✅ | ✅ | ✅ ALL | ✅ | 1G / 1C | ⭐⭐⭐⭐⭐ |
| Notification-Worker | ✅ | ✅ | ✅ ALL | ✅ | 512M / 1C | ⭐⭐⭐⭐⭐ |
| Workflow-Engine | ✅ | ✅ | ✅ ALL | ✅ | 512M / 1C | ⭐⭐⭐⭐⭐ |
| Report-Generator | ✅ | ✅ | ✅ ALL | ✅ | 1.5G / 1C | ⭐⭐⭐⭐⭐ |
| Frontend | ✅ | — | ✅ 3 caps | ✅ | 256M / 0.5C | ⭐⭐⭐⭐ |
| Traefik | Partial (ro) | — | — | ✅ | 256M / 1C | ⭐⭐⭐⭐ |
| ClamAV | ❌ | ❌ | ✅ 3 caps | ✅ | 2G / 2C | ⭐⭐⭐⭐ |
| Backend | ❌ | ❌ | ✅ 3 caps | ✅ | 2G / 2C | ⭐⭐⭐ |
| PostgreSQL | ❌ | ❌ | ✅ 2 caps | ✅ | 2G / 2C | ⭐⭐⭐ |
| Redis | ❌ | ❌ | ✅ 2 caps | ✅ | 1G / 1C | ⭐⭐⭐ |
Cap-Drop "ALL" = all Linux capabilities removed. "3 caps" = NET_RAW, SYS_ADMIN, MKNOD. "2 caps" = NET_RAW, SYS_ADMIN. Resources = memory limit / CPU limit. All containers have JSON logging with rotation (10MB x 3).
5. File Security
Zero-Trust Virus Scan Flow
ZERO-TRUST VIRUS SCAN (7 STEPS):
1. User uploads file
POST /api/attachments/TICKET/:id
↓
2. Backend receives file
• Stores in uploads/ volume
• Creates Attachment record (scanStatus: PENDING)
• NO direct ClamAV scan (zero-trust!)
↓
3. AV-Worker polls (every 10 seconds, SCAN_POLL_CRON)
• Backend returns list of PENDING attachments
↓
4. AV-Worker initiates scan
• TCP 3310 to ClamAV
• Sends file PATH (not content!)
• ClamAV reads from uploads/ volume (read-only)
↓
5. ClamAV scans file
• Returns: CLEAN / INFECTED / ERROR
↓
6. AV-Worker updates Backend
• Reports scan result (e.g. CLEAN)
↓
7. Backend updates Attachment
• scanStatus: PENDING → CLEAN
• If INFECTED:
- Move to quarantine/ volume
- Notify admin
- Block download
SECURITY FEATURES:
✅ av-worker has NO file access (read-only filesystem, no uploads mount)
✅ ClamAV has a read-only mount on uploads/
✅ Backend has read-write, but no scan access
✅ Quarantine volume isolates infected files
📘 Details: See Attachments API → (Zero-Trust Virus Scan Section)
File Upload Security
- Extension Blacklist: .exe, .bat, .sh, .dll, .js, .vbs, .msi, .com, .cmd, .scr blockiert
- MIME-Type Validation: Server-side (not just extension)
- File Size Limits: Global 100MB, per-entity configurable
- Upload Rate Limit: FILE_UPLOAD_RATE_LIMIT: 200/hour/IP (configurable)
- Max Files Limit: Per entity (e.g., max 10 attachments per ticket)
- Retention Policy: Auto-delete after X days (configurable)
- Orphan Cleanup: Delete unused files after 24h
- Soft-Delete: Recovery window (files remain X days after delete)
6. API Security
SSRF Protection
Webhook URLs and external API calls are validated to prevent SSRF attacks. The same check applies on save and at execution (job worker, workflow engine). Invalid allowlist entries are ignored and unlock nothing.
- Allowed schemes: HTTP and HTTPS (no HTTPS-only enforcement), restricted to allowed ports — default 80, 443, 8080, 8443, configurable via
SSRF_ALLOWED_PORTS - Hard-blocked (NEVER allowlistable): localhost, 127.0.0.1, ::1, 0.0.0.0, cloud metadata (169.254.169.254, metadata.google.internal, metadata.azure.com), link-local (fe80::), multicast (ff00::)
- Blocked by default, allowlistable via
SSRF_ALLOWLIST: private ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, IPv6 ULA fc00::/fd00::) — e.g. for internal ERP/monitoring webhooks (IP/CIDR/hostname, no ports) - Teams URLs: Only Microsoft domains allowed (outlook.office.com, outlook.office365.com, webhook.office.com)
📘 Details: See Workflows API →, CronJobs API →, Integrations →
Rate Limiting
| Endpoint/Feature | Limit | Time Window | Type |
|---|---|---|---|
| Traefik Gateway | 100 req/s, burst 200 | Per second | Global (DDoS protection) |
| File uploads | 200 | Per hour | Per IP |
| Email inbound (global) | 60 | Per minute | Global |
| Email inbound (sender) | 30 | Per hour | Per Sender |
| Email max size | 25 MB | - | Per email |
Implementation: Traefik rate limit (gateway level) plus limits in the backend (application level)
📘 Details: See Integrations → (Email Rate Limiting)
Input Validation
- Zod Schemas: All API inputs validated (type-safe)
- SQL Injection: Prisma ORM prevents SQL injection (prepared statements)
- XSS Protection: DOMPurify client-side, CSP headers server-side (Traefik)
- Path Traversal: File paths validated, no ../ allowed
7. Authentication & Authorization
Authentication Methods
| Method | Description | Use Case |
|---|---|---|
| JWT (HttpOnly Cookies) | Standard web login | Portal users (agents, customers) |
| Entra ID SSO | Azure AD OAuth 2.0 | Enterprise single sign-on |
| Native TOTP MFA | HMAC-SHA256 TOTP (FIPS-compatible) | All users (security settings) |
| Cloudflare Turnstile | CAPTCHA bot protection | Login/registration |
| API-Keys | X-API-Key header for external systems | Workflow API trigger, integrations |
| INTERNAL_API_KEY | Shared key the workers use to authenticate to the backend | Inter-container communication |
📘 Details: See Authentication API →
FIPS 140-2 Compatible Cryptography
The backend exclusively uses FIPS 140-2 compatible algorithms. Note: This refers to the use of FIPS-compatible algorithms, not an official FIPS certification. There are two operating modes:
| Component | Algorithm | Details |
|---|---|---|
| Password hashing | PBKDF2-SHA512 | 210,000 iterations, 64-byte key, 32-byte salt |
| 2FA (TOTP) | HMAC-SHA256 | Not SHA-1 (FIPS-compatible) |
| Encryption | AES-256-GCM | License keys, TOTP secrets and stored credentials |
| JWT Signing | HS256 | HMAC-SHA256 |
Operating Modes
| Mode | FIPS-compatible algorithms | FIPS mode enforced | Docker Image |
|---|---|---|---|
| Standard | ✅ | ❌ | node:24-slim |
| Strict FIPS | ✅ | ✅ | Distroless FIPS base image (Node.js 24, OpenSSL 3.0 FIPS Provider) |
In Strict FIPS mode, Node.js runs with --enable-fips and OpenSSL 3.0 FIPS Provider. MD5, MD4, RIPEMD160 are blocked. The startup check (ENABLE_FIPS=true) stops the server immediately if crypto.getFips() !== 1.
FIPS Configuration
| Variable | Default | Description |
|---|---|---|
DOCKERFILE_BACKEND | Dockerfile | Dockerfile.fips for Strict FIPS mode |
ENABLE_FIPS | false | Startup check: server stops if FIPS not active |
PBKDF2_ITERATIONS | 210000 | PBKDF2 iterations (range: 100k–2M) |
UV_THREADPOOL_SIZE | 16 | libuv thread pool for async PBKDF2 |
Password Hashing Security Mechanisms
- Verify Whitelisting: Only sha512, keylen 64, iterations 100k–2M accepted
- Timing-Safe: crypto.timingSafeEqual for password comparisons
- Constant Response Time: a hash is computed for unknown accounts too, so the response time does not reveal whether an account exists
- Automatic Rehash: hashes with a lower iteration count than configured are recomputed on the next successful login
- Hash Format:
pbkdf2:sha512:<iterations>:<keylen>:<salt_base64>:<hash_base64> - Base64 Validation: Strict validation of salt and hash buffers
Native MFA/TOTP
In addition to Entra ID MFA, the system provides native TOTP-based multi-factor authentication:
- Algorithm: HMAC-SHA256 (FIPS-compatible, not SHA-1)
- Secret Encryption: AES-256-GCM via TWO_FACTOR_ENCRYPTION_KEY
- Activation: User security settings (self-service)
- Key Separation: TWO_FACTOR_ENCRYPTION_KEY is separate from JWT_SECRET
Cloudflare Turnstile CAPTCHA
- Protection: Bot protection for login and registration
- CSP Integration: challenges.cloudflare.com allowed in script-src, connect-src, frame-src
- Configuration: TURNSTILE_SITE_KEY, TURNSTILE_SECRET_KEY (env)
JWT Token Security
- HttpOnly Cookies: JavaScript cannot read token (XSS protection)
- Secure Flag: Cookie only sent over HTTPS
- SameSite: CSRF protection
- Token Rotation: JWT_SECRET_OLD supports gradual key rotation
- No Permissions in the Token: permissions are loaded from the role on every request (prevents privilege escalation via stale tokens)
- Expiration: Tokens expire (configurable)
Session Security
| Variable | Default | Description |
|---|---|---|
SESSION_MAX_HOURS | 12 | Hard session end in hours (backend-enforced, not extendable via refresh) |
ACCESS_TOKEN_EXPIRY_MINUTES | 60 | Access token expiry in minutes |
REFRESH_TOKEN_EXPIRY_MINUTES | 100 | Refresh token expiry in minutes (deliberately tight — see rotation below) |
IDLE_TIMEOUT_MINUTES | 30 | Auto-logout on inactivity (server value, enforced in the client) |
COOKIE_SECURE | (NODE_ENV) | Explicitly force/disable the secure flag of the auth cookies |
JWT_SECRET_OLD | - | Previous JWT secret for gradual key rotation |
Refresh token rotation & theft detection
- Rotation on every refresh: A refresh token is a one-time ticket — it is replaced once redeemed.
- Grace window (60 s): If the response is lost (timeout/abort), the old token may briefly be redeemed again — the session does not strand because of a network glitch.
- Reuse detection (10 min): If a long-replaced token reappears afterwards, it counts as stolen: the entire session is terminated immediately, WebSockets disconnected, audit entry REFRESH_TOKEN_REUSE.
- Immediate revoke: Session termination (sessions UI, password change, reuse) takes effect immediately via the stable session ID — regardless of how often the token has rotated since.
- Denied when in doubt: If Redis is unreachable for the blacklist/revoke check, access is denied (503) rather than granted in doubt.
- Terminated stays terminated: Once a session has been terminated, no path ever issues tokens for it again — not even the rotation grace window.
Brute force protection & rate limiting
Protection sits on the TARGET object, not on the IP. This is the key design decision in this area — because behind a corporate NAT all employees share a single IP. An IP limit tight enough to stop an attacker would lock out your own office first.
| Layer | Protection |
|---|---|
| 1. On the object (the real brake) | Per-email account lockout with exponential backoff (5 failures → up to 4 h), CAPTCHA from the 3rd failure, per-user 2FA attempt limit, password reset 1 request / 2 min per email. These limits apply regardless of how many IPs an attacker uses. |
| 2. On the IP (flood backstop) | Login counts per combination of IP AND account (5/15 min) — a colleague with a typo locks only themselves out; plus an IP cap across all accounts (30/15 min) against enumerating many addresses. Refresh (60), logout (120), 2FA/reset (20), invitation lookup (30) and the global cap (2000/min) are deliberately generous: they are meant to brake floods, not guess passwords — 64-character refresh/reset tokens are unguessable anyway. |
Why the login counter keys on (IP + account) and not on the email alone: a pure email counter could be abused to deliberately lock out other people's accounts — you would only have to send a wrong password often enough. The pairing prevents that.
Anyone running into an active account lockout gets the same answer as at every other brake: 429 with a note about too many failed attempts. No CAPTCHA is requested — it would change nothing about an active lockout. The message therefore states what is really the case: a temporary state, not a blocked account.
Two operational notes: the layer-2 IP counters live in the backend instance's memory; a restart resets them. The layer-1 account lockouts live in Redis and survive a restart. And behind an external reverse proxy TRUSTED_PROXIES must be correct — otherwise all requests look like one IP and every IP limit hits everyone at once. All values are ENV-configurable and can be raised without a rebuild: Environment → Rate limits.
Real-time channels check the same visibility
The application keeps detail pages live: whoever has an object open sees the other viewers and receives changes without reloading. It checks the same row-level visibility as the REST endpoint, the list and the global search, from the same source.
- Two levels, both checked: the list channel of a type requires the read permission of that type; the channel of a single object additionally requires visibility of that very row — including asset type locks, mailbox and group restrictions, and the grants of a knowledge article.
- No guessing via IDs: Without visibility of an object you get a rejection on join instead of a viewer list, and a bulk subscription silently filters such objects out. Unknown types are rejected, and the number of objects per subscription is capped.
- Consequence for restricted rows: A link visible only as a placeholder does not update live. Deliberately so: the placeholder reveals nothing about the content, and neither does the live channel.
How the channel works, what users see of it and which object kinds it covers: Real-time & Presence →
8. Compliance & Audit
Enterprise Audit System
- SHA-256 Hash Chain: Each entry is chained to the previous one via SHA-256; tampering is detected on verification
- Immutability: UPDATE/DELETE forbidden (DB trigger)
- PII Scrubbing: Automatically redacts passwords, tokens, email, phone
- 9 Audit Domains: AUTH, ENTITY, ADMIN, SECURITY, SLA, WORKFLOW, SYSTEM, DATA_ACCESS, NOTIFICATION
- 5 Severity Levels: DEBUG, INFO, WARNING, ERROR, CRITICAL
- Redis Fallback: On DB error → Redis backup (7 days TTL)
- Chain Verification API: Quick check & full verification
📘 Details: See Enterprise Audit System →
Supported Standards
| Standard | Supported Requirements |
|---|---|
| FIPS 140-2 | PBKDF2-SHA512, HMAC-SHA256, AES-256-GCM, Strict FIPS mode available |
| ISO 27001 | Audit logging, access control, encryption, incident management |
| SOC 2 | Audit trail, change management, monitoring, security controls |
| GDPR | PII scrubbing, data retention, user anonymization, audit logs |
| NIS2 | Incident reporting, SLA tracking, security monitoring |
Security Best Practices
Secrets Management
- Change All Secrets: JWT_SECRET, POSTGRES_PASSWORD, INTERNAL_API_KEY, LICENSE_ENCRYPTION_KEY, TWO_FACTOR_ENCRYPTION_KEY, REDIS_PASSWORD
- Docker Secrets: Use Docker Secrets (Swarm) or Kubernetes Secrets instead of ENV
- Key Rotation: JWT_SECRET_OLD enables gradual rotation
- LICENSE_ENCRYPTION_KEY: NEVER change after first licenses (data loss!)
- TWO_FACTOR_ENCRYPTION_KEY: NEVER change after first MFA activation (TOTP secrets unreadable!)
- Generation:
openssl rand -hex 32for secure keys
📘 Details: See Environment Variables → (Secret Generation)
Role-Based Access Control (RBAC)
- Least Privilege: Use viewOwn for END_USER, viewAll only for AGENT/ADMIN
- Custom Roles: Create custom roles only when needed (complex to manage)
- Permission Audit: Role changes are logged (ADMIN domain, WARNING severity)
- Critical Actions: tickets.delete, changes.approve always DB revalidation (no cache)
- Permission Cache: 5min TTL, invalidated on role change
Password Policy
- PBKDF2-SHA512: 210,000 iterations (FIPS 140-2 compatible, configurable via PBKDF2_ITERATIONS)
- One-Way Hash: Passwords cannot be decoded
- Password Reset: Secure token via email (time-limited)
- Recommendation: Min. 12 characters, special chars, numbers
Security Features by Feature Area
ITSM Core (Tickets, Incidents, Problems, Changes)
- Permission-Based Visibility: Users see only allowed entities
- Internal Notes: Only visible with viewInternal permission
- Optimistic Locking: Version field prevents race conditions
- Self-Approval Block: Requester ≠ Approver (changes, incidents)
- Activity Timeline: Complete audit trail of all changes
Assets & Inventory
- QR Code Handover: HMAC-Token for public endpoint (time-limited)
- Type-Based Permissions: 6 permissions per asset type
- Financial Data: Separate permission for financial fields
- Checkout Tracking: Who has which asset when (complete history)
📘 Details: See Assets API → (HMAC Token, Type Permissions)
Contracts & Licenses
- AES-256-GCM Encryption: License keys encrypted at-rest
- Format: iv:authTag:encrypted (Base64)
- Key Access Audit: Every decrypt is logged (IP, UserAgent, timestamp)
- viewSensitive Permission: Required for license key access
📘 Details: See Contracts & Licenses API → (AES-256-GCM Encryption)
Workflows & Automation
- SSRF Protection: Webhook actions validated (private IPs only via SSRF_ALLOWLIST, loopback/metadata always blocked)
- Circuit Breaker: Protection against cascade failures (threshold: 5, reset: 30s)
- Timeout Protection: Webhook calls max. 30s
- API-Key Auth: Workflow API triggers require API key
📘 Details: See Workflows API → (SSRF Protection, Circuit Breaker)
Security Incident Response
Detection Mechanisms
- Virus Detection: ClamAV scans ALL uploads (no exceptions)
- Intrusion Detection: Audit events (SECURITY domain) for suspicious activities
- Failed Login Tracking: Audit events (AUTH domain) for brute-force detection
- Permission Violations: Audit events (SECURITY domain, ERROR severity)
Response Actions
- Infected Files: Automatically moved to quarantine volume
- Admin Notification: Multi-channel alert on security events
- User Deactivation: isActive = false (soft-delete, reversible)
- Permission Revocation: Role changes take effect immediately (the role's permission cache is cleared)
- Audit Investigation: Chain verification API for forensics
Security Checklist (Production)
Before Production Start
⚠️ CRITICAL - MUST be changed:
- ☐ JWT_SECRET change (don't use default!)
- ☐ POSTGRES_PASSWORD change (supersecretpassword → secure)
- ☐ JOBWORKER_DB_PASSWORD change
- ☐ READONLY_DB_PASSWORD change
- ☐ INTERNAL_API_KEY change (openssl rand -hex 32)
- ☐ LICENSE_ENCRYPTION_KEY change (NEVER change later!)
- ☐ TWO_FACTOR_ENCRYPTION_KEY change (NEVER change later!)
- ☐ REDIS_PASSWORD change (openssl rand -hex 32)
- ☐ TURNSTILE_SITE_KEY + TURNSTILE_SECRET_KEY configure (Cloudflare dashboard)
- ☐ VAPID_KEYS regenerate (web push)
After Production Start
- ☐ Change admin password (admin@company.com) and enable MFA
- ☐ SEED_DATABASE=false set (no new test users)
- ☐ TLS certificates from Let's Encrypt (not self-signed)
- ☐ Firewall rules (only 80, 443 public)
- ☐ Monitor health checks
- ☐ Test backup strategy (postgres_data, uploads)
- ☐ Regularly check audit logs (chain verification)
- ☐ ClamAV virus defs updated (Freshclam)
- ☐ Consider FIPS mode for regulated environments (DOCKERFILE_BACKEND=Dockerfile.fips)
Related Documentation
- Container Architecture - Container hardening, zero-trust, resource limits, Traefik
- Enterprise Audit System - SHA-256 hash chain, immutability, PII scrubbing
- User Management & RBAC - roles, agent groups, absences
- Authentication API - JWT, Entra ID SSO, TOTP MFA, API keys, token refresh
- Attachments API - Zero-trust virus scan, extension blacklist, MIME validation
- Contracts & Licenses - AES-256-GCM encryption, key access audit
- Integrations - SSRF protection, HMAC signature, rate limiting
- Environment Variables - Secret generation, FIPS configuration, best practices