diff --git a/src/app/(frontend)/api/posts/route.ts b/src/app/(frontend)/api/posts/route.ts index 42995fb..7ef3c6f 100644 --- a/src/app/(frontend)/api/posts/route.ts +++ b/src/app/(frontend)/api/posts/route.ts @@ -4,32 +4,41 @@ import { NextRequest, NextResponse } from 'next/server' import { getPayload } from 'payload' import config from '@payload-config' -import { getPostsByCategory, checkRateLimit } from '@/lib/search' +import { getPostsByCategory } from '@/lib/search' import type { Category } from '@/payload-types' +import { + searchLimiter, + rateLimitHeaders, + getClientIpFromRequest, + isIpBlocked, +} from '@/lib/security' // Validation constants const MAX_LIMIT = 50 const DEFAULT_LIMIT = 10 +const POSTS_RATE_LIMIT = 30 export async function GET(request: NextRequest) { try { - // Rate limiting - const ip = request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() || - request.headers.get('x-real-ip') || - 'unknown' + // IP-Blocklist prüfen + const ip = getClientIpFromRequest(request) + if (isIpBlocked(ip)) { + return NextResponse.json( + { error: 'Access denied' }, + { status: 403 }, + ) + } - const rateLimit = checkRateLimit(ip) + // Rate limiting (zentral) + const rateLimit = await searchLimiter.check(ip) if (!rateLimit.allowed) { return NextResponse.json( { error: 'Too many requests. Please try again later.' }, { status: 429, - headers: { - 'Retry-After': String(rateLimit.retryAfter || 60), - 'X-RateLimit-Remaining': '0', - }, - } + headers: rateLimitHeaders(rateLimit, POSTS_RATE_LIMIT), + }, ) } diff --git a/src/app/(payload)/api/auth/login/route.ts b/src/app/(payload)/api/auth/login/route.ts index ae52f53..be6e1f8 100644 --- a/src/app/(payload)/api/auth/login/route.ts +++ b/src/app/(payload)/api/auth/login/route.ts @@ -26,6 +26,7 @@ import { rateLimitHeaders, getClientIpFromRequest, isIpBlocked, + validateCsrf, } from '@/lib/security' /** @@ -53,6 +54,15 @@ export async function POST(req: NextRequest): Promise { ) } + // CSRF-Schutz für Browser-basierte Requests + const csrfResult = validateCsrf(req) + if (!csrfResult.valid) { + return NextResponse.json( + { success: false, error: 'CSRF validation failed' }, + { status: 403 }, + ) + } + // Rate-Limiting prüfen (Anti-Brute-Force) const rateLimit = await authLimiter.check(clientIp) if (!rateLimit.allowed) { diff --git a/src/app/(payload)/api/send-email/route.ts b/src/app/(payload)/api/send-email/route.ts index 44b403d..8876907 100644 --- a/src/app/(payload)/api/send-email/route.ts +++ b/src/app/(payload)/api/send-email/route.ts @@ -8,6 +8,7 @@ import { rateLimitHeaders, validateIpAccess, createSafeLogger, + validateCsrf, } from '@/lib/security' const RATE_LIMIT_MAX = 10 @@ -74,6 +75,16 @@ export async function POST(req: NextRequest) { ) } + // CSRF-Schutz für Browser-basierte Requests + const csrfResult = validateCsrf(req) + if (!csrfResult.valid) { + logger.warn('CSRF validation failed', { reason: csrfResult.reason }) + return NextResponse.json( + { error: 'CSRF validation failed' }, + { status: 403 }, + ) + } + const payload = await getPayload({ config }) // Authentifizierung prüfen (aus Cookie/Header) diff --git a/src/app/(payload)/api/users/login/route.ts b/src/app/(payload)/api/users/login/route.ts index f77189d..5515092 100644 --- a/src/app/(payload)/api/users/login/route.ts +++ b/src/app/(payload)/api/users/login/route.ts @@ -24,6 +24,7 @@ import { rateLimitHeaders, getClientIpFromRequest, isIpBlocked, + validateCsrf, } from '@/lib/security' /** @@ -50,6 +51,15 @@ export async function POST(req: NextRequest): Promise { ) } + // CSRF-Schutz für Browser-basierte Requests + const csrfResult = validateCsrf(req) + if (!csrfResult.valid) { + return NextResponse.json( + { errors: [{ message: 'CSRF validation failed' }] }, + { status: 403 }, + ) + } + // Rate-Limiting prüfen (Anti-Brute-Force) const rateLimit = await authLimiter.check(clientIp) if (!rateLimit.allowed) {