/** * Constant-time string comparison. * * Use this for every admin-secret / bearer-token / HMAC comparison. Naive * `a === b` short-circuits on the first byte mismatch, leaking length * information that an attacker can use to slow-search the secret. * * `crypto.timingSafeEqual` requires equal-length buffers and runs in * constant time. We normalise to UTF-8 buffers, pad shorter to longer * with zero bytes so length mismatch is also constant-time, and OR a * length-mismatch flag at the end so different lengths can't return true. */ import { timingSafeEqual } from "crypto"; export function timingSafeStringEq(a: string, b: string): boolean { const aBuf = Buffer.from(a, "utf8"); const bBuf = Buffer.from(b, "utf8"); const max = Math.max(aBuf.length, bBuf.length); const aPadded = Buffer.alloc(max); const bPadded = Buffer.alloc(max); aBuf.copy(aPadded); bBuf.copy(bPadded); const equal = timingSafeEqual(aPadded, bPadded); // Length mismatch defeats the compare even if padded prefixes happen to match. return equal && aBuf.length === bBuf.length; }