mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 17:24:12 +00:00
fix: apply CSRF protection and centralize rate limiting
- Migrate /api/posts from legacy checkRateLimit to central searchLimiter - Add IP blocklist check and rateLimitHeaders to /api/posts - Apply CSRF validation to /api/send-email (POST) - Apply CSRF validation to /api/users/login (POST) - Apply CSRF validation to /api/auth/login (POST) CSRF protection uses the Double Submit Cookie pattern which: - Skips safe methods (GET, HEAD, OPTIONS) - Allows server-to-server requests with Authorization header - Validates Origin header for browser requests - Requires matching tokens in header and cookie for browser POSTs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
cb2e903db5
commit
2fae62eaf3
4 changed files with 51 additions and 11 deletions
|
|
@ -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),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {
|
|||
rateLimitHeaders,
|
||||
getClientIpFromRequest,
|
||||
isIpBlocked,
|
||||
validateCsrf,
|
||||
} from '@/lib/security'
|
||||
|
||||
/**
|
||||
|
|
@ -53,6 +54,15 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
|
|||
)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {
|
|||
rateLimitHeaders,
|
||||
getClientIpFromRequest,
|
||||
isIpBlocked,
|
||||
validateCsrf,
|
||||
} from '@/lib/security'
|
||||
|
||||
/**
|
||||
|
|
@ -50,6 +51,15 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
|
|||
)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue