Security is built in from the start, not bolted on.
- Username/password login; passwords hashed with argon2id.
- JWT access tokens (short TTL) + refresh tokens (rotating, stored hashed in
sessions, revocable). Logout revokes the refresh token. Active sessions are listable and individually revocable; the current device is flagged via a session-id (sid) claim. - API tokens for automation — sent as
Authorization: Bearer cc_…, hashed at rest, authenticate as their owner; thecc_prefix disambiguates them from JWTs. - 2FA (TOTP) — implemented: per-user setup/verify/disable, enforced at login, with an admin reset. The TOTP secret is stored encrypted.
| Role | Capabilities |
|---|---|
| Admin | Everything: users, settings, updates, backups, all streams/sources/platforms |
| Operator | Create/run/manage streams, channels, sources, platforms, recordings; no user/system admin |
| Viewer | Read-only: dashboards, monitoring, logs, status |
Every endpoint declares the required role; checks are enforced server-side.
- Encrypted at rest with Fernet (
ENCRYPTION_KEYfrom env, never in DB/repo): stream keys, platform API keys, OAuth refresh tokens, SMB/NFS/cloud credentials, notification secrets. - Write-only over the API: accepted on save, returned masked (
••••1234). - Never logged. Command previews and FFmpeg logs mask secrets (stream keys in URLs, passwords) before display/storage.
- HTTPS enforced via reverse proxy (Caddy auto-cert / Nginx).
- The API is Bearer-token based (no cookie login), so cookie-CSRF does not apply; no dedicated CSRF token is used.
- Rate limiting (implemented): Redis-backed throttling on sensitive auth endpoints
(
login,refresh,2fa/verify, token creation,setup/admin) →429 auth.rate_limited. See the admin security guide for configuration. Broader per-endpoint limits are still TODO. - Security headers (implemented at the reverse proxy): HSTS,
X-Content-Type-Options,Referrer-Policy,X-Frame-Options: DENYand aPermissions-Policy. A Content-Security-Policy is shipped in report-only mode by default (domain-agnostic,'self'-only withblob:/data:for hls.js/thumbnails); toggle viaCSP_ENABLED/CSP_REPORT_ONLY. Enforce mode and a report endpoint are not yet enabled — see the admin security guide.
- No shell execution. FFmpeg/ffprobe/mount/rclone invoked with argument lists
via
subprocess/asyncio (shell=False). No string interpolation into shells. - FFmpeg decoder CVEs (e.g. CVE-2026-8461 / MagicYUV): the installed FFmpeg/ffprobe
version is detected at runtime and a build < 8.1.2 is flagged as vulnerable (system
status, preflight, setup). A safe-media mode flags/blocks risky codecs (default
blocklist:
magicyuv). See FFmpeg requirements. Note: a patched FFmpeg ≥ 8.1.2 is not yet the verified default binary — see that page. - Minimal process environment: spawned FFmpeg processes get no CastCore secrets in their environment (SECRET_KEY/ENCRYPTION_KEY/DB creds are stripped).
- Path traversal prevention: all file/media/mount/recording paths validated and
confined to configured roots (
MEDIA_DIR,MOUNT_DIR,RECORDINGS_DIR, …). - Safe uploads: thumbnails/assets validated by type + size, stored with sanitized, generated filenames; no execution; served with safe content types.
- Mount credentials written to restrictive cred files (
0600, owned bycastcore), never passed on the command line where they'd appear in process listings.
- Audit log for security-relevant actions (login, role change, secret update, start/stop, backup/restore, update) with actor, target, IP, timestamp.
- Restrictive file permissions on data/log/config directories; dedicated unprivileged
system user
castcorefor native installs. - Container hardening:
no-new-privilegeson backend/process-manager/worker; the worker (which processes untrusted media) additionallycap_drop: ALLand runs non-root. The backend and process-manager still run as root (optional in-container CIFS/NFS mounts needSYS_ADMIN; prefer host-side mounts) — full non-root is a known gap.
Implemented: argon2id · JWT rotation + revocation · RBAC on every route · 2FA · API tokens ·
session management · rate limiting (auth endpoints) · Fernet encryption for secret
columns · secrets masked in UI & logs · shell=False everywhere · path confinement · upload
validation · audit log · restrictive file perms · FFmpeg version detection + safe-media mode ·
security headers incl. CSP (report-only by default), X-Frame-Options, Permissions-Policy.
Known gaps: CSP is report-only, not yet enforced, and has no report endpoint (CSRF N/A — Bearer API); broader (non-auth) rate limits; patched FFmpeg ≥ 8.1.2 not yet the verified default; backend/process-manager not yet non-root; OAuth metadata push implemented but not yet verified against the live platform APIs; no frontend/migration tests.