From 829d8cc031a1fb684ddab089ace71c1a74c48501 Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Sun, 10 Nov 2024 19:45:40 +0000 Subject: [PATCH] feat(#79): Add rate limiting to contact page form --- src/lib/utils/rateLimiter.ts | 30 ++++++++++++++++++++++++++++++ src/routes/contact/+page.server.ts | 8 ++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/lib/utils/rateLimiter.ts diff --git a/src/lib/utils/rateLimiter.ts b/src/lib/utils/rateLimiter.ts new file mode 100644 index 0000000..1328b24 --- /dev/null +++ b/src/lib/utils/rateLimiter.ts @@ -0,0 +1,30 @@ +type RateLimitStore = { + [key: string]: { count: number; lastRequest: number }; +}; + +const rateLimitStore: RateLimitStore = {}; +const RATE_LIMIT_WINDOW = 60 * 5000; // 5 minutes +const MAX_REQUESTS = 5; + +export function isRateLimited(ip: string): boolean { + const currentTime = Date.now(); + const requestInfo = rateLimitStore[ip]; + + if (!requestInfo) { + rateLimitStore[ip] = { count: 1, lastRequest: currentTime }; + return false; + } + + if (currentTime - requestInfo.lastRequest > RATE_LIMIT_WINDOW) { + rateLimitStore[ip] = { count: 1, lastRequest: currentTime }; + return false; + } + + if (requestInfo.count >= MAX_REQUESTS) { + return true; + } + + rateLimitStore[ip].count += 1; + rateLimitStore[ip].lastRequest = currentTime; + return false; +} diff --git a/src/routes/contact/+page.server.ts b/src/routes/contact/+page.server.ts index 7073ebc..150a908 100644 --- a/src/routes/contact/+page.server.ts +++ b/src/routes/contact/+page.server.ts @@ -3,9 +3,17 @@ import { fail } from '@sveltejs/kit'; import { sendEmail } from '$lib/utils/brevo/email'; import { VITE_CONTACT_EMAIL } from '$env/static/private'; import logger from '$lib/utils/logger/logger'; +import { isRateLimited } from '$lib/utils/rateLimiter'; export const actions: Actions = { default: async ({ request }) => { + const ip = + request.headers.get('x-forwarded-for') ?? request.headers.get('remote-addr') ?? 'unknown'; + + if (isRateLimited(ip)) { + return fail(429, { error: 'Too many requests. Please try again later.' }); + } + const formData = await request.formData(); const name = formData.get('name') as string; const email = formData.get('email') as string;