security: timing-safe auth to prevent username enumeration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,9 @@ import prisma from '../config/database';
|
|||||||
import { config } from '../config/env';
|
import { config } from '../config/env';
|
||||||
import { AuthData, JwtPayload } from '../types';
|
import { AuthData, JwtPayload } from '../types';
|
||||||
|
|
||||||
|
// Pre-computed bcrypt hash for timing-safe comparison when user not found
|
||||||
|
const DUMMY_HASH = '$2a$12$LJ3m4ys3Lg4oLBFnYP2amuPBzJnJBbGzCl5Y6X9Y8r0q5.s3L6OyO';
|
||||||
|
|
||||||
// --- Token helpers ---
|
// --- Token helpers ---
|
||||||
|
|
||||||
function hashToken(token: string): string {
|
function hashToken(token: string): string {
|
||||||
@@ -79,6 +82,8 @@ export async function login(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
// Timing-safe: run bcrypt even when user not found
|
||||||
|
await bcrypt.compare(password, DUMMY_HASH);
|
||||||
return { type: 'error', message: 'Neplatné přihlašovací údaje', status: 401 };
|
return { type: 'error', message: 'Neplatné přihlašovací údaje', status: 401 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user