Passbolt Security Testing: PGP Key Management, API Authentication, User Provisioning, and Email Exposure

Passbolt is an open-source team password manager built around PGP encryption, used by security-conscious organizations as a self-hosted credential sharing platform. Its security assessment covers several important areas: Passbolt's healthcheck endpoint at /healthcheck is accessible without authentication and exposes database connection status, GPG server key configuration, PHP version, and SSL certificate details — this information disclosure aids targeted exploitation; Passbolt's REST API uses GPG-signed challenge-response authentication — valid API access tokens stored in browser extension local storage or extracted from application context give access to all passwords shared with the authenticated user; Passbolt's LDAP directory synchronization feature (Pro/Cloud) stores LDAP bind credentials in the Passbolt application database — compromise of the Passbolt database yields LDAP service account credentials; account recovery in Passbolt uses administrator-orchestrated key recovery requiring admin cooperation — the account recovery process emails recovery tokens that must be carefully managed; and Passbolt's public key server exposes all user PGP public keys at /api/v1/gpgkeys — this endpoint enables user enumeration and harvesting of all registered user email addresses and key fingerprints without authentication. This guide covers systematic Passbolt security assessment.

Table of Contents

  1. Healthcheck Information Disclosure
  2. User Enumeration via GPG Keys API
  3. API Authentication Token Testing
  4. LDAP Provisioning Credential Exposure
  5. Passbolt Security Hardening

Healthcheck Information Disclosure

# Passbolt healthcheck — accessible without authentication by default
PASSBOLT_URL="https://passbolt.example.com"

# Check healthcheck endpoint (reveals server configuration)
curl -s "${PASSBOLT_URL}/healthcheck" 2>/dev/null | python3 -c "
import json,sys
try:
    d = json.load(sys.stdin)
    print('Healthcheck response:')
    for section, checks in d.items():
        if isinstance(checks, dict):
            print(f'  {section}:')
            for k, v in list(checks.items())[:3]:
                print(f'    {k}: {v}')
except:
    # JSON parse failure — check for HTML response
    content = sys.stdin.read() if hasattr(sys.stdin,'read') else ''
    print(f'HTML response (first 200 chars): {content[:200]}')
" 2>/dev/null

# Alternative: /api/v1/healthcheck.json
curl -s "${PASSBOLT_URL}/api/v1/healthcheck.json" 2>/dev/null | python3 -c "
import json,sys
d = json.load(sys.stdin)
body = d.get('body',{})
print(f'Database: {body.get(\"database\",{}).get(\"connect\",\"?\")}')
print(f'GPG key: {body.get(\"gpg\",{}).get(\"gpgKeyPublicReadable\",\"?\")}')
print(f'SSL: {body.get(\"ssl\",{}).get(\"peerValid\",\"?\")}')
print(f'App version: {body.get(\"application\",{}).get(\"appVersion\",\"?\")}')
" 2>/dev/null

User Enumeration via GPG Keys API

# Passbolt exposes all user PGP public keys without authentication
# This enables user enumeration — harvesting all registered email addresses

PASSBOLT_URL="https://passbolt.example.com"

# List all GPG keys (unauthenticated)
curl -s "${PASSBOLT_URL}/api/v1/gpgkeys.json" 2>/dev/null | python3 -c "
import json,sys
d = json.load(sys.stdin)
keys = d.get('body',[])
print(f'PGP keys registered: {len(keys)} users')
for k in keys[:10]:
    uid = k.get('uid','?')  # Contains 'Name '
    fingerprint = k.get('fingerprint','?')[:16]
    print(f'  {uid} fingerprint={fingerprint}...')
" 2>/dev/null

# Also check if user list is accessible
curl -s "${PASSBOLT_URL}/api/users.json" 2>/dev/null | python3 -c "
import json,sys
d = json.load(sys.stdin)
if d.get('header',{}).get('status') == 'success':
    users = d.get('body',[])
    print(f'Users enumerable: {len(users)}')
    for u in users[:5]:
        profile = u.get('Profile',{})
        print(f'  {profile.get(\"first_name\",\"?\")} {profile.get(\"last_name\",\"?\")} — {u.get(\"username\",\"?\")}')
" 2>/dev/null

Passbolt Security Hardening

Passbolt Security Hardening Checklist:
Security TestMethodRisk
/healthcheck exposes DB status and server configurationGET /healthcheck or /api/v1/healthcheck.json — reveals database connectivity, GPG key status, PHP version, SSL stateMedium
User enumeration via GPG keys APIGET /api/v1/gpgkeys.json — returns all registered user UIDs containing email addresses without authenticationMedium
API JWT token with access to all user-accessible passwordsExtract JWT from browser extension storage — provides API access to all passwords shared with that user accountCritical
LDAP bind credentials in Passbolt configurationAccess /etc/passbolt/passbolt.php or database ldap_directories table — yields LDAP service account credentialsHigh
Brute force on passphrase-less PGP key importIf users configure PGP keys without passphrases, a stolen private key file gives immediate API accessHigh
Account recovery links not revoked after compromiseReview active recovery links in admin panel — unrevoced recovery links from previous compromises remain usableHigh

Automate Passbolt Security Testing

Ironimo tests Passbolt deployments for healthcheck endpoint exposing database connection status, GPG key configuration, and PHP version without authentication, user enumeration via the GPG keys API returning all registered user email addresses without credentials, API JWT tokens stored in browser extension local storage providing access to all user-accessible password shares, LDAP bind credentials stored in the Passbolt database or configuration files, MFA bypass paths for admin accounts, and unrevoced account recovery links remaining valid after user offboarding.

Start free scan