diff --git a/src/auth.js b/src/auth.js index 146c841..f80da4f 100644 --- a/src/auth.js +++ b/src/auth.js @@ -59,6 +59,22 @@ export function requireAdmin(req, res, next) { next(); } +// Set of hostnames a same-site request may originate from. Behind a reverse proxy the +// raw Host header may be the upstream (127.0.0.1:8077), so we also trust the canonical +// host from PUBLIC_BASE_URL and the X-Forwarded-Host the proxy sends. +function allowedHosts(req) { + const hosts = new Set(); + if (req.get('host')) hosts.add(req.get('host')); + const xfh = req.get('x-forwarded-host'); + if (xfh) xfh.split(',').forEach((h) => hosts.add(h.trim())); + try { + if (config.publicBaseUrl) hosts.add(new URL(config.publicBaseUrl).host); + } catch { + /* ignore malformed PUBLIC_BASE_URL */ + } + return hosts; +} + // Lightweight CSRF defense: state-changing requests must originate same-host. export function checkOrigin(req, res, next) { if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) return next(); @@ -66,7 +82,7 @@ export function checkOrigin(req, res, next) { if (origin) { try { const host = new URL(origin).host; - if (host === req.get('host')) return next(); + if (allowedHosts(req).has(host)) return next(); } catch { /* fall through */ }