Jellyseerr Security Testing: API Key, Plex Token, Jellyfin Credentials, and Request Management

Jellyseerr is a fork of Overseerr designed to support both Plex and Jellyfin, widely deployed as the primary media request interface for home lab users and small media server communities. Its security profile extends beyond Jellyseerr itself because it serves as an authentication bridge: Jellyseerr stores the Plex authentication token and Jellyfin admin password in its configuration to communicate with media servers, and these credentials are accessible to anyone with Jellyseerr admin access; Jellyseerr's REST API key in Settings > General provides full read/write access to all media requests, user accounts, and service configurations including Radarr and Sonarr connection details; Jellyseerr allows any authenticated Plex or Jellyfin user to self-register and submit media requests — the scope of who can access the request interface depends on whether the server owner has restricted registration; notification webhooks for request events (approval, availability) make outbound HTTP POST requests creating SSRF; and the /api/v1/user endpoint enumerates all registered users including email addresses. This guide covers systematic Jellyseerr security assessment.

Table of Contents

  1. API Key and Settings Exposure
  2. User Enumeration and Access Control
  3. Notification Webhook SSRF
  4. Jellyseerr Security Hardening

API Key and Settings Exposure

# Jellyseerr API — key-based access to all settings and request data
JELLYSEERR_URL="https://jellyseerr.example.com"
API_KEY="your-jellyseerr-api-key"

# Get main settings — includes API key and configuration
curl -s "${JELLYSEERR_URL}/api/v1/settings/main" \
  -H "X-Api-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'App title: {d.get(\"applicationTitle\")}')
print(f'App URL: {d.get(\"applicationUrl\")}')
print(f'New user login: {d.get(\"newPlexLogin\")}')
print(f'Hide available: {d.get(\"hideAvailable\")}')
" 2>/dev/null

# Get Plex settings — may contain Plex token
curl -s "${JELLYSEERR_URL}/api/v1/settings/plex" \
  -H "X-Api-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Plex hostname: {d.get(\"hostname\")}')
print(f'Plex port: {d.get(\"port\")}')
print(f'Plex token: {d.get(\"authToken\",\"not in response\")}')
" 2>/dev/null

# Get Jellyfin settings — contains admin credentials
curl -s "${JELLYSEERR_URL}/api/v1/settings/jellyfin" \
  -H "X-Api-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Jellyfin hostname: {d.get(\"hostname\")}')
print(f'Jellyfin port: {d.get(\"port\")}')
print(f'Jellyfin API key: {d.get(\"apiKey\",\"not in response\")}')
print(f'Jellyfin user: {d.get(\"jellyfinExternalHost\",\"?\")}')
" 2>/dev/null

# Get all Radarr and Sonarr configurations
curl -s "${JELLYSEERR_URL}/api/v1/settings/radarr" \
  -H "X-Api-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
servers = json.load(sys.stdin)
for s in servers:
    print(f'  Radarr [{s.get(\"id\")}] {s.get(\"name\")} url={s.get(\"hostname\")}:{s.get(\"port\")} apiKey={s.get(\"apiKey\")}')
" 2>/dev/null

User Enumeration and Access Control

# Jellyseerr user enumeration — all registered users accessible via API
JELLYSEERR_URL="https://jellyseerr.example.com"
API_KEY="your-jellyseerr-api-key"

# List all registered users with their details
curl -s "${JELLYSEERR_URL}/api/v1/user?take=100&skip=0" \
  -H "X-Api-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
users = d.get('results',[])
total = d.get('pageInfo',{}).get('results',len(users))
print(f'Users: {total}')
for u in users:
    print(f'  [{u.get(\"id\")}] {u.get(\"displayName\")} email={u.get(\"email\",\"N/A\")} role={u.get(\"permissions\")} requests={u.get(\"requestCount\")}')
" 2>/dev/null

# Get individual user details — includes watch history and request list
USER_ID=1
curl -s "${JELLYSEERR_URL}/api/v1/user/${USER_ID}" \
  -H "X-Api-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
u=json.load(sys.stdin)
print(f'User: {u.get(\"displayName\")} ({u.get(\"email\")})')
print(f'Permissions: {u.get(\"permissions\")}')
print(f'Requests: {u.get(\"requestCount\")}')
print(f'Plex username: {u.get(\"plexUsername\")}')
" 2>/dev/null

Jellyseerr Security Hardening

Jellyseerr Security Hardening Checklist:
Security TestMethodRisk
API key access to all service credentialsGET /api/v1/settings/plex — Plex auth token; GET /api/v1/settings/jellyfin — Jellyfin API key; GET /api/v1/settings/radarr — Radarr API keys; GET /api/v1/settings/sonarr — Sonarr API keysCritical
User enumeration with email addressesGET /api/v1/user with X-Api-Key header — returns displayName, email, plexUsername, permissions, and request count for every registered userHigh
Notification webhook SSRF on request eventsConfigure webhook notification URL to internal address — Jellyseerr server makes outbound POST on request creation/approval/availability events; any registered user submitting a request can trigger the SSRFHigh
Open user registration from any Plex accountAuthenticate with any Plex account and access Jellyseerr — by default, any Plex account holder can self-register and begin submitting media requests; combined with auto-approval settings this may create unauthorized media downloadsMedium
settings.json credential exposureRead /config/settings.json — contains all stored service credentials in plaintext; accessible via path traversal in another service or direct filesystem access to the data directoryHigh

Automate Jellyseerr Security Testing

Ironimo tests Jellyseerr deployments for API key access exposing Plex authentication tokens and Jellyfin API keys, Radarr and Sonarr API key extraction from service settings, user enumeration with email addresses via the user API endpoint, notification webhook SSRF triggered by any request creation event, open user registration from any Plex account holder, and settings.json credential exposure.

Start free scan