security: harden authentication (register/login)
- Username: restrict to [A-Za-z0-9_-], block reserved names (admin, mod, root, etc.), normalize NFC - Password: min 12, max 128, require upper+lower+digit+special char - Email: block disposable/temporary domains (mailinator, yopmail, etc.) - Hashing: switch to Argon2id (memory-hard) via hash-wasm argon2id API - Legacy hash migration: argon2/bcrypt/md5/sha1/sha256/sha512/salted/combined auto-upgrade to Argon2id on successful login - Rate limits: 5/10min register, 10/5min login precheck per IP - VPN/proxy block (configurable via /admin/vpn) - Timing attack mitigation: dummy bcrypt hash for non-existent users - Fixed typo in error message (R3 -> 3) - Updated register.test.ts to match new validation rules
This commit is contained in:
1 parent
6dc80b0b05
commit
ac60a867d9
4 files changed
+83
-22
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { randomBytes } from "node:crypto";
|
||||
import {
|
||||
argon2id,
|
||||
argon2Verify,
|
||||
bcrypt,
|
||||
bcryptVerify,
|
||||
@@ -11,7 +12,26 @@ import {
|
||||
|
||||
import { env } from "@/env";
|
||||
|
||||
/** Argon2id parameters — memory-hard, GPU-resistant. */
|
||||
const ARGON2_CONFIG = {
|
||||
memoryCost: 19456, // ~19 MiB
|
||||
timeCost: 2,
|
||||
parallelism: 1,
|
||||
outputLen: 32,
|
||||
};
|
||||
|
||||
/** Hash new passwords with Argon2id (best practice 2024+). */
|
||||
export async function hashPassword(password: string): Promise<string> {
|
||||
return await argon2id({
|
||||
password,
|
||||
salt: randomBytes(16),
|
||||
...ARGON2_CONFIG,
|
||||
outputType: "encoded",
|
||||
});
|
||||
}
|
||||
|
||||
/** Legacy bcrypt for migrating existing hashes. */
|
||||
export async function hashPasswordBcrypt(password: string): Promise<string> {
|
||||
return await bcrypt({
|
||||
password,
|
||||
salt: randomBytes(16),
|
||||
|
||||
Reference in new issue
Block a user