Skip to content

security: [MEDIUM] Timing side-channel in HTTP auth length check #3201

@louisgv

Description

@louisgv

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    safe-to-workSecurity triage: safe for automated processingsecurity-review-requiredSecurity review found critical/high issues - changes required

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions