diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 7902c442..f50f6630 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -30,3 +30,7 @@ **Vulnerability:** Known high-severity vulnerabilities discovered by the audit in `js-yaml` and `nanoid` packages. **Learning:** Deeply nested dependencies (`js-yaml` via `eslint`, `nanoid` via `vitest/vite`) may expose the application to DoS or logic loops. **Prevention:** Use `pnpm.overrides` in the root `package.json` to enforce patched versions across all transitive paths in a pnpm workspace. +## 2025-03-09 - [Fix User Enumeration via Timing Attack] +**Vulnerability:** User enumeration timing attack in the login route where non-existent users return immediately while existent users incur a ~100ms bcrypt overhead. +**Learning:** Early returns before expensive operations like bcrypt allow attackers to enumerate valid users based on response times. When mitigating this, hardcoding a dummy hash for bcrypt.compare triggers SAST rules (detected-bcrypt-hash). +**Prevention:** Use await bcrypt.hash(password, 10) for non-existent users to safely simulate the required processing delay without failing SAST checks. diff --git a/packages/web/src/lib/server/auth-actions.ts b/packages/web/src/lib/server/auth-actions.ts index 8c48fb26..542bc3e8 100644 --- a/packages/web/src/lib/server/auth-actions.ts +++ b/packages/web/src/lib/server/auth-actions.ts @@ -64,7 +64,10 @@ export async function loginUser( const { email, password } = input const user = await db.user.findUnique({ where: { email } }) - if (!user) return null + if (!user) { + await bcrypt.hash(password, 10) + return null + } const valid = await bcrypt.compare(password, user.passwordHash) if (!valid) return null