Jellyfin is the most widely deployed open-source media server, used as a privacy-respecting alternative to Plex for streaming personal video, music, and photo libraries. Its security profile has several important characteristics: Jellyfin's initial setup wizard is accessible before admin account creation — a newly deployed or factory-reset instance that hasn't completed setup allows any visitor to claim the admin account and take full control of the media server; Jellyfin's dashboard-generated API keys provide persistent server access without a login session, have no expiration date by default, and are stored in the Jellyfin database — a leaked API key represents long-lived unauthorized access to the media library and server configuration; Jellyfin's DLNA service broadcasts server presence via SSDP on UDP port 1900, making the media server discoverable to all devices on the local network without authentication; Jellyfin supports creating user accounts with no password or very weak passwords — these accounts allow any client application to authenticate with just the username; and Jellyfin's remote access feature when enabled exposes the server's media streaming API to the internet. This guide covers systematic Jellyfin security assessment.
# Jellyfin setup wizard accessible on new/reset instances
JELLYFIN_URL="https://media.example.com"
# Check if setup wizard is accessible (returns 200 if not yet configured)
STARTUP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
"${JELLYFIN_URL}/Startup/Configuration" \
-H "X-Emby-Authorization: MediaBrowser Client=\"SecurityTest\", Device=\"Test\", DeviceId=\"test123\", Version=\"10.8.0\"" \
2>/dev/null)
echo "Startup endpoint: ${STARTUP_STATUS}"
# 200 = setup wizard accessible — admin takeover possible
# Check server info (always accessible, reveals version)
curl -s "${JELLYFIN_URL}/System/Info/Public" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Server: {d.get(\"ServerName\",\"?\")}')
print(f'Version: {d.get(\"Version\",\"?\")}')
print(f'Product: {d.get(\"ProductName\",\"?\")}')
" 2>/dev/null
# Jellyfin API keys: created in Dashboard → API Keys
# No expiration by default — persistent access even after password change
API_KEY="your-jellyfin-api-key"
JELLYFIN_URL="https://media.example.com"
# Verify API key provides server access
curl -s "${JELLYFIN_URL}/Users?api_key=${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
if isinstance(d,list):
print(f'Users on server: {len(d)}')
for u in d[:5]:
print(f\" {u.get('Name','?')} admin={u.get('Policy',{}).get('IsAdministrator','?')}\")
" 2>/dev/null
# Enumerate media libraries accessible with this key
curl -s "${JELLYFIN_URL}/Library/VirtualFolders?api_key=${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
if isinstance(d,list):
for lib in d:
print(f\"Library: {lib.get('Name','?')} type={lib.get('CollectionType','?')} paths={lib.get('Locations',[])}\")
" 2>/dev/null
| Security Test | Method | Risk |
|---|---|---|
| Setup wizard accessible before admin creation | GET /Startup/Configuration — 200 response means admin account can be claimed by any visitor; completes setup and takes full server control | Critical |
| User accounts with empty passwords | POST /Users/AuthenticateByName with username and empty pw — accounts created for household use often have no password configured | Critical |
| API keys with no expiration | API key in query string gives full server access indefinitely — leaked keys from automation scripts or config files represent permanent access | High |
| DLNA/SSDP media catalog discovery | UDP discovery on port 1900 — SSDP M-SEARCH reveals Jellyfin server presence, version, and basic media library structure on local network | Medium |
| Public /System/Info/Public endpoint | GET /System/Info/Public — always accessible without auth, reveals server name, version, and operating system details for attack planning | Low |
| Remote access without additional authentication | Jellyfin accessible from internet without VPN or reverse proxy auth layer — exposes user auth API and media streaming to brute force | High |
Ironimo tests Jellyfin deployments for accessible setup wizard enabling admin account takeover before configuration is complete, user accounts with empty or weak passwords via the AuthenticateByName endpoint, API keys with no expiration date created for integrations and accessible from leaked configuration, DLNA/SSDP media catalog disclosure on the local network, /System/Info/Public version disclosure aiding attack planning, and remote access configuration exposing the media streaming API without additional authentication controls.
Start free scan