From 2170dd774343a68a50ba360e65cb16e3a8975aa2 Mon Sep 17 00:00:00 2001 From: you Date: Sat, 21 Mar 2026 18:40:06 +0000 Subject: [PATCH] security: require API key for POST /api/packets and /api/perf/reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New config.apiKey field — when set, POST endpoints require X-Api-Key header - If apiKey not configured, endpoints remain open (dev/local mode) - GET endpoints and /api/decode (read-only) remain public - Closes the packet injection attack surface --- config.example.json | 1 + server.js | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/config.example.json b/config.example.json index 948cf53c..1a0f55ea 100644 --- a/config.example.json +++ b/config.example.json @@ -1,5 +1,6 @@ { "port": 3000, + "apiKey": "your-secret-api-key-here", "https": { "cert": "/path/to/cert.pem", "key": "/path/to/key.pem" diff --git a/server.js b/server.js index fdbd4130..3bb5e616 100644 --- a/server.js +++ b/server.js @@ -29,6 +29,15 @@ const MAX_HOP_DIST_SERVER = config.maxHopDist || 1.8; const crypto = require('crypto'); const PacketStore = require('./packet-store'); +// API key middleware for write endpoints +const API_KEY = config.apiKey || null; +function requireApiKey(req, res, next) { + if (!API_KEY) return next(); // no key configured = open (dev mode) + const provided = req.headers['x-api-key'] || req.query.apiKey; + if (provided === API_KEY) return next(); + return res.status(401).json({ error: 'Invalid or missing API key' }); +} + // Compute a content hash from raw hex: header byte + payload (skipping path hops) // This correctly groups retransmissions of the same packet (same content, different paths) function computeContentHash(rawHex) { @@ -317,7 +326,7 @@ app.get('/api/perf', (req, res) => { }); }); -app.post('/api/perf/reset', (req, res) => { perfStats.reset(); res.json({ ok: true }); }); +app.post('/api/perf/reset', requireApiKey, (req, res) => { perfStats.reset(); res.json({ ok: true }); }); // --- Event Loop Lag Monitoring --- let evtLoopLag = 0, evtLoopMax = 0, evtLoopSamples = []; @@ -965,7 +974,7 @@ app.post('/api/decode', (req, res) => { } }); -app.post('/api/packets', (req, res) => { +app.post('/api/packets', requireApiKey, (req, res) => { try { const { hex, observer, snr, rssi, region, hash } = req.body; if (!hex) return res.status(400).json({ error: 'hex is required' });