From bacf1d2fcb415cdffc509ee1ca9bbb5bd0112041 Mon Sep 17 00:00:00 2001 From: Mitchell R Date: Sun, 10 May 2026 02:59:04 +0200 Subject: [PATCH] fix: use deterministic HKDF key for cookie signing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cookieMac was using encryptString which generates a random IV per call, making the HMAC key non-deterministic. Cookie signed at login could never verify on subsequent requests. Now uses deriveKey(info) which uses HKDF — deterministic for same server key. --- server/src/shared/auth.ts | 5 +++-- server/src/shared/secrets.ts | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/server/src/shared/auth.ts b/server/src/shared/auth.ts index 9eb1a73..c11a215 100644 --- a/server/src/shared/auth.ts +++ b/server/src/shared/auth.ts @@ -170,9 +170,10 @@ export function createAuth( // ---- Sessions ------------------------------------------------------------- + const cookieKey = secrets.deriveKey("cookie"); + function cookieMac(sid: string): string { - const subkeyMaterial = secrets.encryptString("cookie-subkey", "cookie-derivation"); - return createHmac("sha256", subkeyMaterial).update(sid).digest("hex"); + return createHmac("sha256", cookieKey).update(sid).digest("hex"); } function signCookie(sid: string): string { diff --git a/server/src/shared/secrets.ts b/server/src/shared/secrets.ts index d1239b7..30a7510 100644 --- a/server/src/shared/secrets.ts +++ b/server/src/shared/secrets.ts @@ -33,6 +33,7 @@ export interface SecretsLog { export interface SecretsApi { encryptString(plaintext: string, info?: string): string; decryptString(ciphertext: string, info?: string): string; + deriveKey(info: string): Buffer; generateClusterKey(): string; encryptForCluster(plaintext: string, clusterKeyB64u: string): string; } @@ -54,6 +55,10 @@ export function initSecrets(config: SecretsConfig, log: SecretsLog): SecretsApi } return { + deriveKey(info: string): Buffer { + return deriveSubkey(info); + }, + encryptString(plaintext: string, info: string = "field"): string { const subkey = deriveSubkey(info); const iv = randomBytes(12);