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>
This commit is contained in:
21
README.md
21
README.md
@@ -59,6 +59,11 @@ Immich Folio acts as a **secure reverse proxy** between your visitors and your p
|
||||
|
||||
</details>
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Immich 3.0 or newer.** Immich 3.0 changed how album assets are retrieved; earlier versions are not supported as of v0.9.0. On an older server, albums render with the correct title but no photos, and the map stays empty — the app logs a warning naming this as the likely cause.
|
||||
- Node.js 20+ (or just use the Docker image)
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
@@ -91,19 +96,23 @@ IMMICH_API_KEY=your-api-key
|
||||
SITE_TITLE=My Photography # default: "Gallery"
|
||||
SITE_SUBTITLE=A visual journal # default: empty
|
||||
CACHE_TTL=300 # seconds, default: 300
|
||||
RATE_LIMIT_RPM=120 # requests/min/IP, default: 120
|
||||
RATE_LIMIT_RPM=1500 # requests/min/IP, default: 1500
|
||||
AUTH_SECRET=long-random-string # required in production
|
||||
TRUSTED_PROXY_HOPS=1 # reverse proxies in front, default: 0
|
||||
ADMIN_PASSWORD=your-secure-password # enables /admin panel
|
||||
```
|
||||
|
||||
> Behind a reverse proxy, set `TRUSTED_PROXY_HOPS` to the number of proxies in front of the app (nginx/Traefik/Caddy = 1; Cloudflare in front of nginx = 2). Without it the client IP is read from a header the client itself can set, which defeats the brute-force limits on the password endpoints. See [Trusted Proxies](docs/gallery-config.md#trusted-proxies).
|
||||
|
||||
### Immich API Key Permissions
|
||||
|
||||
Create a dedicated API key in Immich under **Account Settings → API Keys**. Immich Folio only needs **read access** — it never modifies your library.
|
||||
|
||||
| Permission | Required | Used for |
|
||||
|---|---|---|
|
||||
| `album.read` | ✅ Yes | List and fetch album metadata & photo lists |
|
||||
| `asset.read` | ✅ Yes | Fetch asset metadata, EXIF data, thumbnails, previews, and originals |
|
||||
| `asset.view` | ✅ Yes | Stream image/video files (thumbnail, preview, video playback) |
|
||||
| Permission | Required | Used for |
|
||||
| ------------ | -------- | -------------------------------------------------------------------- |
|
||||
| `album.read` | ✅ Yes | List and fetch album metadata & photo lists |
|
||||
| `asset.read` | ✅ Yes | Fetch asset metadata, EXIF data, thumbnails, previews, and originals |
|
||||
| `asset.view` | ✅ Yes | Stream image/video files (thumbnail, preview, video playback) |
|
||||
|
||||
> **No write permissions needed.** `album.create`, `asset.upload`, `asset.delete`, etc. can all be left **off**.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user