Files
immich-folio/.env.local.example
Ralksta 37f7f62157 security: fix pre-auth bypasses and restore Immich 3.x compatibility (#378)
Seven security issues from an audit, plus album pages and the map coming
up empty against Immich 3.x. Verified against a real Immich 3.0.3
instance in a production build.

Security:
- Admin session forgery via a hardcoded 'dev-fallback-secret' signing key
  that bypassed the production AUTH_SECRET guard
- /api/map ignored album passwords and returned working asset tokens;
  rewriting ?size=thumbnail to ?size=original downloaded full-resolution
  originals from protected albums
- Admin login had no rate limit, and the client IP came from a spoofable
  header, so rotating it bypassed every limit in the app
- Raw Immich asset UUIDs were serialized into the RSC payload
- Album tokens never expired server-side; a captured token replayed forever
- Path traversal into the Immich API via an unvalidated album id
- /admin ran with no enforced CSP (only Report-Only reached it)

TRUSTED_PROXIES is replaced by TRUSTED_PROXY_HOPS. The old variable
matched proxy IPs against the socket peer address, which self-hosted
Next.js never exposes, so the check could never pass: unset, the limiter
read a spoofable header; set, every request shared one bucket and a single
client could rate-limit the whole site. Migrates automatically.

Immich 3.x: GET /albums/:id no longer embeds assets. Assets now come from
POST /search/metadata with withExif: true — without that flag EXIF is
omitted and grid aspect ratios, the lightbox panel and map markers stay
empty. Drops support for Immich below 3.0, which now logs a warning.

Upgrade: requires Immich 3.0+; set TRUSTED_PROXY_HOPS (nginx = 1);
visitors of protected areas and admin sessions must log in once more.

91 unit tests (25 new); the new admin-auth and rate-limit cases were
confirmed to fail against the pre-fix code.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 18:57:57 +02:00

49 lines
2.4 KiB
Plaintext

# ── Immich Server Connection (required) ──────────────────────────
# Validated by Zod at startup — missing or invalid values produce clear errors.
# Base URL of your Immich instance (must be a valid URL)
IMMICH_API_URL=http://your-immich-server:2283
# Immich API key (from Admin → API Keys)
IMMICH_API_KEY=your-api-key-here
# ── Optional Settings ────────────────────────────────────────────
# All have sensible defaults and are type-checked by Zod.
# Site title shown in the header (string, default: "Gallery")
SITE_TITLE=My Photography
# Subtitle displayed below the title (string, default: "")
SITE_SUBTITLE=A visual journal
# Cache TTL in seconds (integer >= 0, default: 300 = 5 minutes)
CACHE_TTL=300
# Rate limit for image proxy (integer >= 1, requests per minute per IP, default: 120)
# RATE_LIMIT_RPM=120
# Number of reverse proxies in front of the app (nginx/Traefik/Caddy = 1,
# Cloudflare in front of nginx = 2). Required for correct rate limiting behind
# a proxy — without it the client IP is read from a spoofable header.
# The app must be reachable ONLY through the proxy (bind to 127.0.0.1).
# TRUSTED_PROXY_HOPS=1
# Secret for signing auth cookies and encrypting asset tokens (string, min 16 chars).
# If not provided, IMMICH_API_KEY will be used as a fallback (not recommended).
# AUTH_SECRET=your-super-long-random-secret-here
# Webhook secret for cache invalidation (optional, string).
# Set this to a long random string and configure Immich to send webhooks to
# POST /api/webhook with an x-immich-signature header (HMAC-SHA256 of the body).
# If not set, the webhook endpoint returns 501 and is effectively disabled.
# WEBHOOK_SECRET=your-long-random-webhook-secret
# ── Admin Panel ──────────────────────────────────────────────────
# Set a password to enable the visual admin panel at /admin.
# Leave unset or empty to disable the admin UI entirely.
# ADMIN_PASSWORD=your-secure-admin-password
# ── Album configuration ──────────────────────────────────────────
# Albums, subpages, hero image, and passwords are defined in content/gallery.yaml
# See content/gallery.yaml.example for the full reference.