Security hardening for production readiness

- Add security headers (CSP, HSTS, X-Frame-Options, X-Content-Type-Options, etc.)
- Strengthen password requirements (10+ chars, mixed case, numbers)
- Increase shared list slug entropy from 4 to 16 bytes
- Add rate limiting to login, registration, upload, and restore endpoints
- Add file magic number validation for image uploads (JPEG, PNG, WebP, HEIC)
- Add CSV row limit (50k) to restore endpoint
- Update client-side registration form to match new password policy

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
JP Scott
2026-03-01 12:55:16 -07:00
parent 969bc9347a
commit 8a582bfa7f
8 changed files with 149 additions and 10 deletions

View File

@@ -4,6 +4,7 @@ import GitHub from "next-auth/providers/github"
import Credentials from "next-auth/providers/credentials"
import { PrismaAdapter } from "@auth/prisma-adapter"
import { prisma } from "@/lib/prisma"
import { rateLimit } from "@/lib/rate-limit"
const providers = [
Google({
@@ -25,6 +26,10 @@ const providers = [
const password = credentials?.password as string
if (!email || !password) return null
// Rate limit: 10 login attempts per email per 15 minutes
const rl = rateLimit(`login:${email.toLowerCase()}`, 10, 15 * 60 * 1000)
if (!rl.success) return null
const user = await prisma.user.findUnique({ where: { email } })
if (!user || !user.password) return null

View File

@@ -652,7 +652,7 @@ export async function executeRestore(
.map((oldId) => drinkIdMap.get(oldId))
.filter(Boolean) as string[]
const slug = crypto.randomBytes(6).toString("hex")
const slug = crypto.randomBytes(16).toString("hex")
if (mode === "replace") {
await tx.sharedList.create({