Baserow Security Testing: Default Credentials, API Token, Database, and .env SECRET_KEY

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.

Table of Contents

  1. Authentication and API Token Testing
  2. API Table and Row Data Enumeration
  3. .env Credential Extraction and Database Access
  4. Baserow Security Hardening

Authentication and API Token Testing

# 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

API Table and Row Data Enumeration

# 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

.env Credential Extraction and Database Access

# 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

Baserow Security Hardening

Baserow Security Hardening Checklist:
Security TestMethodRisk
Django SECRET_KEY extraction and session forgeryRead .env SECRET_KEY — Django session signing key; forge valid session cookies as any Baserow user bypassing password authenticationCritical
API table row data enumerationGET /api/database/rows/table/{id} with API token — all table rows including all user-defined data fields; complete export of any Baserow database tableHigh
database_token table plaintext token extractionPostgreSQL SELECT from database_token — plaintext API token values for all users; enables API access as any user without their passwordCritical
DATABASE_URL credential extraction and PostgreSQL accessRead .env DATABASE_URL — full connection string with password; direct access to all Baserow data including user accounts and all table dataCritical
Admin user email enumerationGET /api/user/admin/users — all registered users with emails, active status, last login; enables targeted phishing against all platform usersHigh

Automate Baserow Security Testing

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