Baserow is a widely deployed open-source no-code database platform (Airtable alternative) built on Django and Nuxt.js, used by teams to store structured data, build internal tools, and manage business data without writing code — it stores all user-defined tables and their data. Key assessment areas: SECRET_KEY in .env is Django's secret key used for session signing and CSRF token generation; DATABASE_URL contains the PostgreSQL connection string with all Baserow metadata and user data; the Baserow REST API allows enumeration of all tables and their row data; API tokens are stored in the database_token table; and admin-level endpoints expose user email enumeration. This guide covers systematic Baserow security assessment.
# Baserow — authentication and API token testing
BASEROW_URL="https://baserow.example.com"
# Baserow login — JWT-based authentication
AUTH_RESPONSE=$(curl -s -X POST "${BASEROW_URL}/api/user/token-auth/" \
-H "Content-Type: application/json" \
-d '{"email":"admin@example.com","password":"admin"}' 2>/dev/null)
TOKEN=$(echo "$AUTH_RESPONSE" | python3 -c "
import json,sys
d=json.load(sys.stdin)
if 'access_token' in d:
print(d['access_token'])
elif 'token' in d:
print(d['token'])
else:
print(f'FAIL: {list(d.keys())}')
" 2>/dev/null)
echo "JWT: ${TOKEN}"
# Try common default passwords
for PASS in "admin" "baserow" "password" "changeme" "123456"; do
STATUS=$(curl -s -X POST "${BASEROW_URL}/api/user/token-auth/" \
-H "Content-Type: application/json" \
-d "{\"email\":\"admin@example.com\",\"password\":\"${PASS}\"}" 2>/dev/null | \
python3 -c "import json,sys; d=json.load(sys.stdin); print('OK' if 'access_token' in d or 'token' in d else 'FAIL')" 2>/dev/null)
echo "admin@example.com/${PASS}: ${STATUS}"
done
# Baserow database API token (for row-level API access)
API_TOKEN="your-database-api-token"
# Test API token permissions
curl -s "${BASEROW_URL}/api/database/tables/1/fields/" \
-H "Authorization: Token ${API_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Fields: {len(d)} in table 1')
for f in d[:10]:
print(f' [{f.get(\"id\")}] {f.get(\"name\")} type={f.get(\"type\")}')
" 2>/dev/null
# Baserow REST API — table and row data enumeration
BASEROW_URL="https://baserow.example.com"
JWT_TOKEN="your-jwt-token"
API_TOKEN="your-database-api-token"
# List all workspaces (groups in older versions)
curl -s "${BASEROW_URL}/api/workspaces/" \
-H "Authorization: JWT ${JWT_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
workspaces = d if isinstance(d,list) else d.get('results',[])
print(f'Workspaces: {len(workspaces)}')
for w in workspaces:
print(f' [{w.get(\"id\")}] {w.get(\"name\")} members={len(w.get(\"users\",[]))}')
" 2>/dev/null
# List all applications (databases) in a workspace
WORKSPACE_ID=1
curl -s "${BASEROW_URL}/api/applications/?workspace_id=${WORKSPACE_ID}" \
-H "Authorization: JWT ${JWT_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
apps = d if isinstance(d,list) else d.get('results',[])
print(f'Applications: {len(apps)}')
for a in apps:
print(f' [{a.get(\"id\")}] {a.get(\"name\")} type={a.get(\"type\")} tables={len(a.get(\"tables\",[]))}')
for t in a.get('tables',[]):
print(f' Table [{t.get(\"id\")}]: {t.get(\"name\")}')
" 2>/dev/null
# Get all rows from a table via database API token
TABLE_ID=1
curl -s "${BASEROW_URL}/api/database/rows/table/${TABLE_ID}/?page=1&size=100" \
-H "Authorization: Token ${API_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
rows = d.get('results',[])
total = d.get('count',len(rows))
print(f'Rows: {total} total, showing {len(rows)}')
for r in rows[:5]:
print(f' {r}')
" 2>/dev/null
# Admin endpoint — list all users
curl -s "${BASEROW_URL}/api/user/admin/users/?page=1&page_size=100" \
-H "Authorization: JWT ${JWT_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
users = d.get('results',[])
print(f'Users: {d.get(\"count\")} total')
for u in users[:10]:
print(f' [{u.get(\"id\")}] {u.get(\"email\")} name={u.get(\"name\")} staff={u.get(\"is_staff\")} active={u.get(\"is_active\")}')
" 2>/dev/null
# Baserow .env credential extraction and database access
# .env file — Django secrets and database credentials
cat /baserow/.env 2>/dev/null | grep -E "SECRET_KEY|DATABASE_URL|REDIS_URL|EMAIL|AWS|STORAGE"
# SECRET_KEY — Django secret key (session signing, CSRF tokens)
# DATABASE_URL — PostgreSQL connection string with password
# REDIS_URL — Redis connection for Celery tasks
# Docker deployment
docker inspect baserow 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','DATABASE','REDIS','EMAIL','AWS','SMTP']):
print(e)
" 2>/dev/null
# PostgreSQL direct access
psql "$DATABASE_URL" 2>/dev/null << 'EOF'
-- All Baserow users
SELECT id, email, name, is_staff, is_active, date_joined, last_login
FROM auth_user
ORDER BY is_staff DESC, date_joined DESC
LIMIT 20;
EOF
# database_token — API tokens stored in plaintext
psql "$DATABASE_URL" 2>/dev/null << 'EOF'
SELECT dt.id, dt.key, dt.name,
u.email as owner_email,
dt.created, dt.handled_tables_count
FROM database_token dt
JOIN auth_user u ON dt.user_id = u.id
ORDER BY dt.created DESC
LIMIT 20;
EOF
# key column — plaintext API token value
# All workspace members
psql "$DATABASE_URL" 2>/dev/null << 'EOF'
SELECT gm.id, u.email, u.name, gm.permissions,
g.name as workspace_name
FROM core_workspaceuser gm
JOIN auth_user u ON gm.user_id = u.id
JOIN core_workspace g ON gm.workspace_id = g.id
ORDER BY g.name, gm.permissions
LIMIT 30;
EOF
python -c "import secrets; print(secrets.token_hex(50))"; restrict .env to 600 owned by the Baserow process user; never commit .env files to version control; use Docker secrets or a secrets manager in production; rotate SECRET_KEY if exposed (invalidates all active sessions)| Security Test | Method | Risk |
|---|---|---|
| Django SECRET_KEY extraction and session forgery | Read .env SECRET_KEY — Django session signing key; forge valid session cookies as any Baserow user bypassing password authentication | Critical |
| API table row data enumeration | GET /api/database/rows/table/{id} with API token — all table rows including all user-defined data fields; complete export of any Baserow database table | High |
| database_token table plaintext token extraction | PostgreSQL SELECT from database_token — plaintext API token values for all users; enables API access as any user without their password | Critical |
| DATABASE_URL credential extraction and PostgreSQL access | Read .env DATABASE_URL — full connection string with password; direct access to all Baserow data including user accounts and all table data | Critical |
| Admin user email enumeration | GET /api/user/admin/users — all registered users with emails, active status, last login; enables targeted phishing against all platform users | High |
Ironimo tests Baserow deployments for Django SECRET_KEY extraction and session forgery, REST API workspace/application/table enumeration, table row data extraction, database_token table plaintext token harvesting, admin user email enumeration, DATABASE_URL PostgreSQL credential extraction, Docker environment variable exposure, Redis network exposure testing, public registration misconfiguration, and workspace member permission audit.
Start free scan