From f1e1ad73ade498a4c988f23e11895648272d7459 Mon Sep 17 00:00:00 2001 From: David Rudduck <47308254+davidrudduck@users.noreply.github.com> Date: Thu, 19 Feb 2026 21:16:35 +1000 Subject: [PATCH] fix(security): SHA-256 hash before timingSafeEqual to prevent length leak (#20856) The previous implementation returned early when buffer lengths differed, leaking the expected secret's length via timing side-channel. Hashing both inputs with SHA-256 before comparison ensures fixed-length buffers and constant-time comparison regardless of input lengths. --- src/security/secret-equal.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/security/secret-equal.ts b/src/security/secret-equal.ts index 4ea80b321f1..4e0e3ead29b 100644 --- a/src/security/secret-equal.ts +++ b/src/security/secret-equal.ts @@ -1,4 +1,4 @@ -import { timingSafeEqual } from "node:crypto"; +import { createHash, timingSafeEqual } from "node:crypto"; export function safeEqualSecret( provided: string | undefined | null, @@ -7,10 +7,6 @@ export function safeEqualSecret( if (typeof provided !== "string" || typeof expected !== "string") { return false; } - const providedBuffer = Buffer.from(provided); - const expectedBuffer = Buffer.from(expected); - if (providedBuffer.length !== expectedBuffer.length) { - return false; - } - return timingSafeEqual(providedBuffer, expectedBuffer); + const hash = (s: string) => createHash("sha256").update(s).digest(); + return timingSafeEqual(hash(provided), hash(expected)); }