From 406a43ff2c81fdd04027781a9b7856493945621c Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 12 Oct 2026 00:54:04 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=97=B4=EA=B1=B0=20=ED=83=80=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EA=B3=B5=EA=B2=A9=20=EB=B0=A9=EC=96=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 존재하지 않는 사용자일 경우에도 bcrypt.hash를 수행하여 존재하는 사용자의 비밀번호 검증(bcrypt.compare)과 동일한 수준의 지연을 발생시켜 타이밍 공격을 통한 사용자 열거 취약점을 방어함. --- .jules/sentinel.md | 4 ++++ packages/web/src/lib/server/auth-actions.ts | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) 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