Thanks for taking the time to report security issues responsibly.
Please do NOT open a public GitHub issue for security bugs. A public report gives attackers a head start before users can upgrade.
Use one of the following private channels:
- GitHub Private Vulnerability Reporting (preferred) - open https://github.com/ZL154/JellyfinSecurity/security/advisories/new and submit the advisory. Only repository maintainers can see it.
- Discord - send a friend request to
@zack154on Discord, then DM the description once it is accepted. Discord drops DMs between people with no mutual server, so the request has to come first.
Both paths above can be closed from a reporter's side: private vulnerability reporting has to be enabled on the repository before that form accepts anything, and a Discord DM waits on a friend request being accepted.
If you hit either wall, still do not describe the issue in public. Open a normal issue that contains no details at all - no affected endpoint, no version range, no reproduction - saying only that you have a security finding and asking for a channel. That is enough to start the conversation and gives away nothing to anyone reading along.
- Affected plugin version(s) (e.g.
2.3.0) - Affected Jellyfin server version(s) (e.g.
10.11.8) - Steps to reproduce (PoC welcome)
- Impact assessment (auth bypass, RCE, info disclosure, DoS, etc.)
- Suggested fix or mitigation if you have one
- Initial acknowledgement
- CVE assignment for confirmed vulnerabilities (via GitHub Security Advisories)
- Credit in the release notes and advisory unless you ask to remain anonymous
The following are generally not treated as vulnerabilities in this plugin:
- Issues that require an attacker to already be a Jellyfin administrator
- Issues caused by misconfigured deployments (e.g. running Jellyfin
without HTTPS in front of it, missing
TrustForwardedFor+TrustedProxyCidrsbehind a reverse proxy) - Self-XSS or theoretical timing attacks against TOTP without a working PoC
- Reports against unsupported plugin versions (see table below)
- Findings from automated scanners with no exploitable PoC
In scope:
- Password compromise - blocked by 2FA / passkey / OIDC requirement
- Brute-force login - blocked by per-IP rate limit + IP ban
- Stolen session cookie replay - bound to device, revocable
- Trusted-browser cookie forgery - HMAC-SHA256 signed, length-checked
- Replay of recovery codes - single-use, marked used on validation
- TOTP code replay across restarts - persisted last-used time-step floor
- TOTP secret swap between users on disk - AES-GCM with userId as AAD
- OIDC token forgery - signature + issuer + audience + nonce validated
- Stolen X-Forwarded-For header - only honoured when peer is a trusted proxy CIDR; real client picked right-to-left
- Impossible travel - notification when consecutive sign-ins exceed cruise-jet speed
- IP allowlist bypass - high-value accounts can be pinned to CIDRs
Out of scope (intentional limitations):
- Endpoint compromise (keylogger reads the user's TOTP code before they type it)
- Server compromise (root on the Jellyfin host)
- Browser-extension compromise stealing the trust cookie
- Side-channel attacks on the underlying OS / crypto primitives
- Native client compromise (a malicious Swiftfin fork that exfiltrates app passwords)
- TOTP secret encryption: AES-GCM with a persistent 32-byte key, userId as AAD (v2 format)
- Trust cookie signing: HMAC-SHA256 with persistent key, length-checked
before
CryptographicOperations.FixedTimeEquals - Recovery code hashing: PBKDF2-HMAC-SHA256, 600k iterations, per-code salt (legacy 100k records remain valid and are consumed on use)
- App password hashing: PBKDF2-SHA256
- API key hashing: SHA-256 (raw key is 256 bits of entropy, no PBKDF2 needed)
- Audit log integrity: per-entry hash chain (
prev_hash || entry) - Constant-time comparison for every secret-bearing path
If you find a deviation from any of the above, that itself is in scope.