From 4b71e995e87093ca7ae066428b15c82f9ced9e08 Mon Sep 17 00:00:00 2001 From: Ramkumar kushwah <68776478+kushwahramkumar2003@users.noreply.github.com> Date: Mon, 21 Oct 2024 02:27:34 +0530 Subject: [PATCH] bug: Security vulnerabilities in external Login API endpoint (#1417) fix: Fix timing attacks in externalLogin route --- .../api/admin/services/externalLogin/route.ts | 55 ++++++++++++++----- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/src/app/api/admin/services/externalLogin/route.ts b/src/app/api/admin/services/externalLogin/route.ts index d3f34761b..d0f7da68a 100644 --- a/src/app/api/admin/services/externalLogin/route.ts +++ b/src/app/api/admin/services/externalLogin/route.ts @@ -1,7 +1,13 @@ +import { z } from 'zod'; import db from '@/db'; import { NextRequest, NextResponse } from 'next/server'; import bcrypt from 'bcrypt'; +const loginSchema = z.object({ + email: z.string(), + password: z.string(), +}); + export async function POST(req: NextRequest) { const authKey = req.headers.get('Auth-Key'); @@ -11,11 +17,19 @@ export async function POST(req: NextRequest) { try { const payload = await req.json(); - const { email, password } = payload; + + const result = loginSchema.safeParse(payload); + if (!result.success) { + return NextResponse.json( + { message: 'Invalid input', errors: result.error.errors }, + { status: 400 }, + ); + } + + const { email, password } = result.data; + const user = await db.user.findFirst({ - where: { - email, - }, + where: { email }, select: { id: true, email: true, @@ -23,34 +37,45 @@ export async function POST(req: NextRequest) { password: true, }, }); - if (!user) { - return NextResponse.json({ message: 'User not found' }, { status: 404 }); + + // Perform a dummy bcrypt compare if user doesn't exist to prevent timing attacks + if (!user || !user.password) { + await bcrypt.compare(password, await bcrypt.hash('dummy', 10)); + return NextResponse.json( + { message: 'Invalid credentials' }, + { status: 401 }, + ); } - if ( - user && - user.password && - (await bcrypt.compare(password, user.password)) - ) { + const isPasswordValid = await bcrypt.compare(password, user.password); + + if (isPasswordValid) { const courses = await db.course.findMany({ where: { purchasedBy: { some: { - user: { - email, - }, + user: { email }, }, }, }, }); + return NextResponse.json({ message: 'User found', data: { - user, + user: { + id: user.id, + email: user.email, + name: user.name, + }, courses, }, }); } + return NextResponse.json( + { message: 'Invalid credentials' }, + { status: 401 }, + ); } catch (error) { return NextResponse.json( { message: 'Error fetching user' },