Browse Source

fix: use try-catch on insert to handle TOCTOU username race condition

Replaced select-then-insert with direct insert wrapped in try-catch.
The UNIQUE constraint on username catches duplicate registrations
atomically, returning a clean error message instead of leaking
a raw DB exception.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
feat/registration-page
npmrun 2 weeks ago
parent
commit
4d507df75d
  1. 36
      server/api/auth/register.post.ts

36
server/api/auth/register.post.ts

@ -2,7 +2,7 @@ import { registerSchema } from '../../utils/auth/validation'
import { verifyCaptcha } from '../../utils/auth/captcha' import { verifyCaptcha } from '../../utils/auth/captcha'
import { dbGlobal } from 'drizzle-pkg/lib/db' import { dbGlobal } from 'drizzle-pkg/lib/db'
import { users } from 'drizzle-pkg/lib/schema/auth' import { users } from 'drizzle-pkg/lib/schema/auth'
import { eq } from 'drizzle-orm'
import { hash } from 'bcryptjs' import { hash } from 'bcryptjs'
export default defineWrappedResponseHandler(async (event) => { export default defineWrappedResponseHandler(async (event) => {
@ -19,25 +19,21 @@ export default defineWrappedResponseHandler(async (event) => {
return R.error('验证码错误或已过期', null) return R.error('验证码错误或已过期', null)
} }
const existing = await dbGlobal
.select()
.from(users)
.where(eq(users.username, username))
if (existing.length > 0) {
return R.error('用户名已存在', null)
}
const hashedPassword = await hash(password, 10) const hashedPassword = await hash(password, 10)
const result = await dbGlobal try {
.insert(users) const result = await dbGlobal
.values({ .insert(users)
username, .values({
password: hashedPassword, username,
role: 'user', password: hashedPassword,
status: 'active', role: 'user',
}) status: 'active',
.returning({ id: users.id }) })
.returning({ id: users.id })
return R.success({ id: result[0].id, username })
return R.success({ id: result[0].id, username })
} catch {
return R.error('用户名已存在', null)
}
}) })

Loading…
Cancel
Save