-
Notifications
You must be signed in to change notification settings - Fork 15
security: [MEDIUM] Timing side-channel in HTTP auth length check #3201
Copy link
Copy link
Open
Labels
safe-to-workSecurity triage: safe for automated processingSecurity triage: safe for automated processingsecurity-review-requiredSecurity review found critical/high issues - changes requiredSecurity review found critical/high issues - changes required
Description
Issue
The HTTP authentication function isHttpAuthed() performs a length comparison before the timing-safe equality check, which could leak information about the secret length via timing side-channels.
Location
File: .claude/skills/setup-spa/main.ts
Lines: 1316-1322
Vulnerable Code
function isHttpAuthed(req: Request): boolean {
if (!TRIGGER_SECRET) return false;
const given = req.headers.get("Authorization") ?? "";
const expected = `Bearer ${TRIGGER_SECRET}`;
if (given.length !== expected.length) return false; // ← Timing leak
return timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}Severity
MEDIUM — The length check before timingSafeEqual could leak the secret length through timing analysis. While this is low practical risk (secrets should be high-entropy anyway), it violates timing-safety best practices.
Recommendation
Remove the explicit length check and rely solely on timingSafeEqual, which handles length mismatches securely:
function isHttpAuthed(req: Request): boolean {
if (!TRIGGER_SECRET) return false;
const given = req.headers.get("Authorization") ?? "";
const expected = `Bearer ${TRIGGER_SECRET}`;
// timingSafeEqual will return false for length mismatches securely
if (given.length !== expected.length) {
// Pad shorter buffer to avoid throwing
const maxLen = Math.max(given.length, expected.length);
const givenBuf = Buffer.alloc(maxLen);
const expectedBuf = Buffer.alloc(maxLen);
Buffer.from(given).copy(givenBuf);
Buffer.from(expected).copy(expectedBuf);
return timingSafeEqual(givenBuf, expectedBuf);
}
return timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}Or simpler (Node.js timingSafeEqual throws on length mismatch, so wrap it):
function isHttpAuthed(req: Request): boolean {
if (!TRIGGER_SECRET) return false;
const given = req.headers.get("Authorization") ?? "";
const expected = `Bearer ${TRIGGER_SECRET}`;
try {
return timingSafeEqual(Buffer.from(given), Buffer.from(expected));
} catch {
return false; // Length mismatch or other error
}
}References
- CWE-208: Observable Timing Discrepancy
- OWASP: Timing Attack
-- code-scanner
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
safe-to-workSecurity triage: safe for automated processingSecurity triage: safe for automated processingsecurity-review-requiredSecurity review found critical/high issues - changes requiredSecurity review found critical/high issues - changes required