← back to Dw Boardroom Governance
Harden basic-auth middleware: timing-safe compare, malformed-header guard, fail-closed on unset creds
79f893e8890bf7291ea65dcb57a052f1a26b2b13 · 2026-05-18 20:17:15 -0700 · Steve Abrams
Files touched
M src/api/middleware/auth.ts
Diff
commit 79f893e8890bf7291ea65dcb57a052f1a26b2b13
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon May 18 20:17:15 2026 -0700
Harden basic-auth middleware: timing-safe compare, malformed-header guard, fail-closed on unset creds
---
src/api/middleware/auth.ts | 24 +++++++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/src/api/middleware/auth.ts b/src/api/middleware/auth.ts
index bd1fb37..dc00240 100644
--- a/src/api/middleware/auth.ts
+++ b/src/api/middleware/auth.ts
@@ -1,4 +1,13 @@
import { Request, Response, NextFunction } from 'express';
+import { timingSafeEqual } from 'crypto';
+
+function safeEqual(a: string, b: string): boolean {
+ const bufA = Buffer.from(a);
+ const bufB = Buffer.from(b);
+ // timingSafeEqual requires equal-length buffers; bail early on mismatch.
+ if (bufA.length !== bufB.length) return false;
+ return timingSafeEqual(bufA, bufB);
+}
export function authMiddleware(req: Request, res: Response, next: NextFunction): void {
const auth = req.headers.authorization;
@@ -9,10 +18,19 @@ export function authMiddleware(req: Request, res: Response, next: NextFunction):
return;
}
- const decoded = Buffer.from(auth.split(' ')[1], 'base64').toString();
- const [user, pass] = decoded.split(':');
+ const token = auth.split(' ')[1] || '';
+ const decoded = Buffer.from(token, 'base64').toString();
+ // Split on the first colon only — passwords may legitimately contain colons.
+ const sep = decoded.indexOf(':');
+ const user = sep >= 0 ? decoded.slice(0, sep) : '';
+ const pass = sep >= 0 ? decoded.slice(sep + 1) : '';
+
+ const expectedUser = process.env.AUTH_USER;
+ const expectedPass = process.env.AUTH_PASS;
- if (user === process.env.AUTH_USER && pass === process.env.AUTH_PASS) {
+ // Fail closed if server credentials are not configured.
+ if (expectedUser && expectedPass &&
+ safeEqual(user, expectedUser) && safeEqual(pass, expectedPass)) {
next();
return;
}
← a6dac82 Fix meeting phase auto-advance to use per-phase durations in
·
back to Dw Boardroom Governance
·
Fix useWebSocket: prevent zombie reconnect timer firing afte 44b2fa8 →