Jellyfin Security Testing: Default Admin Setup, API Key Exposure, DLNA/SSDP, and Media Access Control

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.

Table of Contents

  1. Setup Wizard Admin Takeover
  2. API Key Exposure
  3. User Account Password Testing
  4. DLNA/SSDP Discovery
  5. Jellyfin Security Hardening

Setup Wizard Admin Takeover

# 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

API Key Exposure

# 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

Jellyfin Security Hardening

Jellyfin Security Hardening Checklist:
Security TestMethodRisk
Setup wizard accessible before admin creationGET /Startup/Configuration — 200 response means admin account can be claimed by any visitor; completes setup and takes full server controlCritical
User accounts with empty passwordsPOST /Users/AuthenticateByName with username and empty pw — accounts created for household use often have no password configuredCritical
API keys with no expirationAPI key in query string gives full server access indefinitely — leaked keys from automation scripts or config files represent permanent accessHigh
DLNA/SSDP media catalog discoveryUDP discovery on port 1900 — SSDP M-SEARCH reveals Jellyfin server presence, version, and basic media library structure on local networkMedium
Public /System/Info/Public endpointGET /System/Info/Public — always accessible without auth, reveals server name, version, and operating system details for attack planningLow
Remote access without additional authenticationJellyfin accessible from internet without VPN or reverse proxy auth layer — exposes user auth API and media streaming to brute forceHigh

Automate Jellyfin Security Testing

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