From efc9e6510c90d246592762d2c433ba4cfd29c938 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 21 Apr 2026 12:11:23 +0200 Subject: [PATCH] feat: optional API key auth via X-API-Key header or ?key= param --- CLAUDE.md | 18 ++++++++++++++++++ web/index.php | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index e0c8eff..158c454 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -46,6 +46,24 @@ php -S localhost:8080 -t web/ | `docker/start.sh` | Boots PHP-FPM, ChromeDriver, writes config | | `docker-compose.yml` | Production compose | +## API Key + +Set `API_KEY` env var in docker-compose to require authentication on all `/api` requests. +Leave unset (default) for open access. + +```bash +# via header (preferred — not logged in access logs) +curl -H "X-API-Key: your-secret-key" "http://host/api?url=https://example.com" + +# via query param +curl "http://host/api?key=your-secret-key&url=https://example.com" +``` + +## SSRF Protection + +Set `BLOCK_PRIVATE_IPS=true` to reject requests to LAN, loopback, and cloud metadata IPs. +Recommended when hosting publicly. Default is off (allows local/LAN addresses). + ## Caveats - `web/index.php` has a `var_dump($cmd)` debug statement left in `http2pic.class.php:181` - remove before shipping. diff --git a/web/index.php b/web/index.php index dd1297c..42e8e33 100755 --- a/web/index.php +++ b/web/index.php @@ -24,6 +24,14 @@ if (php_sapi_name() == 'cli-server' && file_exists(ROOT . DS . 'web' . DS . impl switch ($url[0]) { case 'api': + if (defined('API_KEY') && API_KEY !== '') { + $provided = $_SERVER['HTTP_X_API_KEY'] ?? $_REQUEST['key'] ?? ''; + if ($provided !== API_KEY) { + header('HTTP/1.0 401 Unauthorized'); + echo 'Invalid or missing API key'; + exit; + } + } $target = substr($_SERVER['REQUEST_URI'], 5); if (!$target || !filter_var($target, FILTER_VALIDATE_URL)) $target = $_REQUEST['url'];