PocketBase is a popular open-source Go-based backend-as-a-service with a built-in SQLite database, file storage, authentication, and real-time subscriptions — distributed as a single binary it is widely used in indie applications and prototypes. Its security assessment covers: the admin dashboard at /_/ requires a superuser setup on first access — if deployment scripts or Docker images don't enforce this, the dashboard may be accessible to setup by any attacker who reaches it; PocketBase collection API rules determine who can read, create, update, and delete records — the common development pattern of setting rules to empty string (allow all) for rapid prototyping is frequently forgotten in production deployments; PocketBase's JavaScript hooks execute in a JSVM environment with access to $os, $http, and other bindings enabling server-side operations — admin users with hook creation access have server-side code execution; uploaded files are served from deterministic paths based on record ID and filename, making file contents accessible if the collection view rule allows unauthenticated access; and PocketBase's /api/collections endpoint lists all collection schemas to admin users and can reveal sensitive data model information. This guide covers systematic PocketBase security assessment.
# PocketBase admin dashboard at /_/
# On first launch: /_/ shows a setup form — CRITICAL if accessible before admin is created
# Check if PocketBase is accessible and in setup mode
SETUP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
"https://pocketbase.example.com/_/" 2>/dev/null)
echo "Admin dashboard HTTP status: ${SETUP_STATUS}"
# Check if superuser setup has been completed
curl -s "https://pocketbase.example.com/api/health" 2>/dev/null | \
python3 -c "
import json,sys
d = json.load(sys.stdin)
print(f\"PocketBase status: {d.get('code','?')} {d.get('message','?')}\")
" 2>/dev/null
# Test admin authentication endpoint with common weak credentials
for CRED in "admin@example.com:password" "admin@admin.com:admin" "test@test.com:password"; do
EMAIL="${CRED%%:*}"
PASS="${CRED##*:}"
RESPONSE=$(curl -s -X POST \
"https://pocketbase.example.com/api/admins/auth-with-password" \
-H "Content-Type: application/json" \
-d "{\"identity\":\"${EMAIL}\",\"password\":\"${PASS}\"}" 2>/dev/null)
if echo "$RESPONSE" | python3 -c "
import json,sys
d=json.load(sys.stdin)
if d.get('token'):
print(f'LOGIN SUCCESS with {\"$EMAIL\"}/{\"$PASS\"}')
print(f'Token: {d[\"token\"][:20]}...')
" 2>/dev/null; then
break
fi
done
# PocketBase API rules: empty string = allow all (most permissive)
# Common developer mistake: setting "" during development and forgetting to restrict
# Attempt to list all records from common collection names without auth
COLLECTIONS=("users" "posts" "articles" "products" "orders" "messages"
"comments" "settings" "config" "todos" "notes" "files")
for COL in "${COLLECTIONS[@]}"; do
RESPONSE=$(curl -s \
"https://pocketbase.example.com/api/collections/${COL}/records" \
2>/dev/null)
STATUS=$(echo "$RESPONSE" | python3 -c "
import json,sys
d = json.load(sys.stdin)
if 'items' in d:
total = d.get('totalItems', 0)
print(f'OPEN: {total} records accessible without auth')
elif 'code' in d:
print(f'Protected ({d[\"code\"]})')
else:
print('Unknown response')
" 2>/dev/null)
echo "/api/collections/${COL}/records: ${STATUS}"
done
# With admin token: list all collections and their API rules
ADMIN_TOKEN="your-admin-token"
curl -s "https://pocketbase.example.com/api/collections" \
-H "Authorization: ${ADMIN_TOKEN}" 2>/dev/null | \
python3 -c "
import json,sys
d = json.load(sys.stdin)
for col in d.get('items', []):
rules = col.get('listRule','?')
print(f\"{col['name']:30} listRule='{rules}' {'<-- OPEN' if rules=='' else ''}\")
" 2>/dev/null
@request.auth.id != "" to require authentication; consider field-level rules for sensitive data$os access can execute OS commands; review all hooks for dangerous operations and restrict hook creation to trusted administrators| Security Test | Method | Risk |
|---|---|---|
| Admin dashboard in setup mode accessible to unauthenticated users | GET /_/ — if no superuser configured, attacker creates admin account with full control | Critical |
| Collection API rules set to allow all (empty string) | GET /api/collections/{name}/records without auth — open rule returns all records unauthenticated | High |
| Users collection readable without authentication | GET /api/collections/users/records — exposes all user emails, names, and profile data | High |
| JS hook code execution via admin JSVM | Create hook with $os.exec() via admin dashboard — OS command execution on PocketBase server | Critical |
| Uploaded files accessible without authentication | GET /api/files/{collection}/{record}/{filename} — deterministic path; if view rule is open, all files downloadable | High |
| Weak admin credentials | POST /api/admins/auth-with-password with common passwords — admin token grants full collection management | Critical |
Ironimo tests PocketBase deployments for admin dashboard in setup mode allowing unauthenticated attacker-controlled superuser creation, collection API rules configured with empty string allowing all unauthenticated data access, users collection readable without authentication exposing all user emails and profile data, JS hook JSVM code execution capabilities accessible to admin users including $os bindings for OS-level operations, uploaded files accessible without authentication via predictable /api/files/ path patterns, and weak admin credentials susceptible to brute force via the admin authentication endpoint.
Start free scan