feat(security): add Redis-backed rate limiting and CSP/security headers
Public go-live hardening: Redis-backed rate limiting on the previously invite-gated public surface (explore browsing, post/comment creation, invite-cookie routes) plus CSP, HSTS, and standard security headers on every response, now that the invite gate no longer filters traffic.
This commit is contained in:
1 parent
ec9573f33a
commit
f43541a4cc
11 files changed
+162
-8
No files matched your search
@@ -3,6 +3,8 @@ import { NextRequest, NextResponse } from "next/server";
|
||||
import { cookies } from "next/headers";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { generateInviteCode, INVITE_CODES_PER_USER } from "@/lib/invite-codes";
|
||||
import { checkRateLimit } from "@/lib/rate-limit";
|
||||
import { getClientIp } from "@/lib/get-client-ip";
|
||||
|
||||
function resolveBase(request: NextRequest): string {
|
||||
const proto = request.headers.get("x-forwarded-proto") ?? "https";
|
||||
@@ -17,6 +19,15 @@ export async function GET(request: NextRequest) {
|
||||
const { userId } = await auth();
|
||||
const base = resolveBase(request);
|
||||
|
||||
const { success } = await checkRateLimit({
|
||||
key: `auth.consume-invite:${getClientIp(request)}`,
|
||||
limit: 20,
|
||||
windowSeconds: 60,
|
||||
});
|
||||
if (!success) {
|
||||
return NextResponse.redirect(new URL("/", base));
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
return NextResponse.redirect(new URL("/log-in", base));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { checkRateLimit } from "@/lib/rate-limit";
|
||||
import { getClientIp } from "@/lib/get-client-ip";
|
||||
|
||||
function resolveBase(request: NextRequest): string {
|
||||
const proto = request.headers.get("x-forwarded-proto") ?? "https";
|
||||
@@ -11,9 +13,19 @@ function resolveBase(request: NextRequest): string {
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const code = request.nextUrl.searchParams.get("code")?.trim().toUpperCase();
|
||||
const base = resolveBase(request);
|
||||
|
||||
const { success } = await checkRateLimit({
|
||||
key: `auth.set-invite:${getClientIp(request)}`,
|
||||
limit: 20,
|
||||
windowSeconds: 60,
|
||||
});
|
||||
if (!success) {
|
||||
return NextResponse.redirect(new URL("/join?error=rate-limited", base));
|
||||
}
|
||||
|
||||
const code = request.nextUrl.searchParams.get("code")?.trim().toUpperCase();
|
||||
|
||||
if (!code) {
|
||||
return NextResponse.redirect(new URL("/join?error=missing", base));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user