From 8154e904b9c011194b2563cb318a40fcf2276f91 Mon Sep 17 00:00:00 2001 From: Summit Dental Care Date: Mon, 22 Jun 2026 23:53:28 -0400 Subject: [PATCH] security: restrict network-backup endpoints to LAN IPs only Requests from outside 10.x, 192.168.x, 172.16-31.x, 127.x are rejected with 403 before the API key is even checked. This prevents the database dump endpoint from being reachable from the internet even if the key leaked. Co-Authored-By: Claude Sonnet 4.6 --- .../src/routes/network-backup-public.ts | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/apps/Backend/src/routes/network-backup-public.ts b/apps/Backend/src/routes/network-backup-public.ts index 7e3065bf..0565fcb8 100644 --- a/apps/Backend/src/routes/network-backup-public.ts +++ b/apps/Backend/src/routes/network-backup-public.ts @@ -9,7 +9,27 @@ const router = Router(); const UPLOADS_DIR = path.resolve(process.cwd(), "uploads"); -function checkApiKey(req: Request, res: Response): boolean { +function isLanIp(ip: string): boolean { + return ( + /^127\./.test(ip) || + /^10\./.test(ip) || + /^192\.168\./.test(ip) || + /^172\.(1[6-9]|2\d|3[01])\./.test(ip) + ); +} + +function checkAccess(req: Request, res: Response): boolean { + // Only allow requests from the local network + const ip = + (req.headers["x-real-ip"] as string) || + (req.headers["x-forwarded-for"] as string)?.split(",")[0].trim() || + req.socket.remoteAddress || + ""; + if (!isLanIp(ip)) { + res.status(403).json({ error: "Access restricted to local network" }); + return false; + } + const providedKey = req.headers["x-network-backup-key"] as string | undefined; if (!providedKey) { res.status(401).json({ error: "Missing X-Network-Backup-Key header" }); @@ -25,7 +45,7 @@ function checkApiKey(req: Request, res: Response): boolean { // GET /api/database-management/network-backup router.get("/network-backup", async (req: Request, res: Response): Promise => { - if (!checkApiKey(req, res)) return; + if (!checkAccess(req, res)) return; const pg = spawn( "pg_dump", @@ -59,7 +79,7 @@ router.get("/network-backup", async (req: Request, res: Response): Promise // GET /api/database-management/network-backup-files router.get("/network-backup-files", (req: Request, res: Response): any => { - if (!checkApiKey(req, res)) return; + if (!checkAccess(req, res)) return; if (!fs.existsSync(UPLOADS_DIR)) { return res.status(200).end();