From 53006601ba6c10f9e33f1940672b838820af18eb Mon Sep 17 00:00:00 2001 From: Kenan KARAER Date: Fri, 2 Jan 2026 22:33:24 +0300 Subject: [PATCH] =?UTF-8?q?hata=20d=C3=BCzeltme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.ts | 6 ++ src/app/(auth)/login/actions.ts | 16 +---- src/lib/captcha.ts | 109 -------------------------------- src/lib/security.ts | 96 ---------------------------- src/lib/supabase/session.ts | 15 ++--- 5 files changed, 13 insertions(+), 229 deletions(-) delete mode 100644 src/lib/captcha.ts diff --git a/next.config.ts b/next.config.ts index 354a29b..114fa69 100644 --- a/next.config.ts +++ b/next.config.ts @@ -10,6 +10,12 @@ const nextConfig: NextConfig = { port: '', pathname: '/storage/v1/object/public/**', }, + { + protocol: 'https', + hostname: 'api-weeding.edoysoft.com', + port: '', + pathname: '/storage/v1/object/public/**', + }, { protocol: 'https', hostname: 'img.youtube.com', diff --git a/src/app/(auth)/login/actions.ts b/src/app/(auth)/login/actions.ts index 7b8a790..3649673 100644 --- a/src/app/(auth)/login/actions.ts +++ b/src/app/(auth)/login/actions.ts @@ -3,8 +3,7 @@ import { revalidatePath } from 'next/cache' import { redirect } from 'next/navigation' import { createClient } from '@/lib/supabase/server' -import { sendOTP } from '../verify/actions' -import { checkRateLimit, incrementRateLimit, logActivity } from '@/lib/security' +import { logActivity } from '@/lib/security' export type LoginState = { error?: string @@ -17,30 +16,19 @@ export async function login(prevState: LoginState, formData: FormData): Promise< const email = formData.get('email') as string const password = formData.get('password') as string - // 1. Check Rate Limit - const { blocked, resetTime } = await checkRateLimit('login_attempt') - if (blocked) { - return { error: `Çok fazla hatalı deneme. Lütfen ${resetTime?.toLocaleTimeString()} sonrası tekrar deneyin.` } - } - const { data: { user }, error } = await supabase.auth.signInWithPassword({ email, password, }) if (error) { - await incrementRateLimit('login_attempt') await logActivity(null, 'login_failed', { email, error: error.message }) return { error: 'Giriş yapılamadı. E-posta veya şifre hatalı.' } } await logActivity(user?.id || null, 'login_success', { email }) - revalidatePath('/', 'layout') revalidatePath('/', 'layout') - // Trigger OTP email - await sendOTP() - - redirect('/verify') + redirect('/dashboard') } diff --git a/src/lib/captcha.ts b/src/lib/captcha.ts deleted file mode 100644 index 86ad531..0000000 --- a/src/lib/captcha.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { createHmac } from 'crypto' - -export interface CaptchaData { - image: string // SVG string - hash: string // HMAC hash of the text -} - -const CAPTCHA_SECRET = process.env.CAPTCHA_SECRET || 'default-secret-change-me' - -export function generateCaptcha(width = 200, height = 80): { text: string, data: string } { - console.log('[Captcha] Generating new captcha...') - // 1. Generate random text (5 chars) - const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789' // Removed confusing chars like I, 1, 0, O - let text = '' - for (let i = 0; i < 5; i++) { - text += chars.charAt(Math.floor(Math.random() * chars.length)) - } - console.log('[Captcha] Generated text:', text) - - // 2. Create SVG - const bg = '#f3f4f6' - const fg = '#374151' - - // Random noise lines - let noise = '' - for (let i = 0; i < 7; i++) { - const x1 = Math.random() * width - const y1 = Math.random() * height - const x2 = Math.random() * width - const y2 = Math.random() * height - noise += `` - } - - // Random noise dots - for (let i = 0; i < 30; i++) { - const x = Math.random() * width - const y = Math.random() * height - noise += `` - } - - // Text with slight rotation/position randomization - let svgText = '' - const fontSize = 32 - const startX = 20 - const spacing = 35 - - for (let i = 0; i < text.length; i++) { - const char = text[i] - const x = startX + (i * spacing) + (Math.random() * 10 - 5) - const y = (height / 2) + (fontSize / 3) + (Math.random() * 10 - 5) - const rotate = Math.random() * 40 - 20 // +/- 20 degrees - - svgText += `${char}` - } - - const svg = ` - - - ${noise} - ${svgText} -` - - return { text, data: svg } -} - -export function signCaptcha(text: string): string { - const expires = Date.now() + 5 * 60 * 1000 // 5 minutes - const data = `${text.toUpperCase()}|${expires}` - const signature = createHmac('sha256', CAPTCHA_SECRET).update(data).digest('hex') - return `${data}|${signature}` -} - -export function verifyCaptcha(input: string, hash: string): boolean { - if (!input || !hash) { - console.log('[Captcha] Missing input or hash') - return false - } - - const parts = hash.split('|') - if (parts.length !== 3) { - console.log('[Captcha] Invalid hash format') - return false - } - - const [originalText, expiresStr, signature] = parts - const expires = parseInt(expiresStr, 10) - - // Check expiration - if (Date.now() > expires) { - console.log('[Captcha] Expired. Now:', Date.now(), 'Expires:', expires) - return false - } - - // Check signature integrity - const expectedData = `${originalText}|${expiresStr}` - const expectedSignature = createHmac('sha256', CAPTCHA_SECRET).update(expectedData).digest('hex') - - if (signature !== expectedSignature) { - console.log('[Captcha] Signature mismatch') - return false - } - - // Check content match - const isValid = input.toUpperCase() === originalText - if (!isValid) { - console.log('[Captcha] Text mismatch. Expected:', originalText, 'Got:', input.toUpperCase()) - } - return isValid -} diff --git a/src/lib/security.ts b/src/lib/security.ts index ee3cbb2..31050e4 100644 --- a/src/lib/security.ts +++ b/src/lib/security.ts @@ -33,100 +33,4 @@ export async function logActivity( } } -export async function checkRateLimit(action: string): Promise<{ blocked: boolean, remaining?: number, resetTime?: Date }> { - const MAX_ATTEMPTS = 5 - try { - const supabase = await createAdminClient() || await createClient() - const headersList = await headers() - let ip = headersList.get("x-forwarded-for") || headersList.get("x-real-ip") || 'unknown' - if (ip.includes(',')) ip = ip.split(',')[0].trim() - if (ip === '::1') ip = '127.0.0.1' - - // Clean up old limits - // Clean up old limits (logic simplified, variable unused) - - // Check current limit - const { data: limit } = await supabase - .from('rate_limits') - .select('*') - .eq('ip_address', ip) - .eq('action', action) - .single() - - if (!limit) { - return { blocked: false, remaining: MAX_ATTEMPTS } - } - - // If blocked - if (limit.blocked_until && new Date(limit.blocked_until) > new Date()) { - return { blocked: true, resetTime: new Date(limit.blocked_until) } - } - - // If window expired, reset code handling happens in increment logic usually. - // But here we just check. - // Actually, simpler logic: - // We will increment on failure. This function just checks if currently blocked. - - return { blocked: false, remaining: MAX_ATTEMPTS - (limit.count || 0) } - - } catch (error) { - console.error('Rate limit check failed:', error) - return { blocked: false } // Fail open - } -} - -export async function incrementRateLimit(action: string) { - const BLOCK_DURATION_MINUTES = 15 - - try { - const supabase = await createAdminClient() || await createClient() - const headersList = await headers() - let ip = headersList.get("x-forwarded-for") || headersList.get("x-real-ip") || 'unknown' - if (ip.includes(',')) ip = ip.split(',')[0].trim() - if (ip === '::1') ip = '127.0.0.1' - - const { data: limit } = await supabase - .from('rate_limits') - .select('*') - .eq('ip_address', ip) - .eq('action', action) - .single() - - if (limit) { - // Check if we should reset (if last attempt was long ago) - const lastAttempt = new Date(limit.last_attempt) - const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000) - - let newCount = limit.count + 1 - let blockedUntil = null - - if (lastAttempt < tenMinutesAgo) { - newCount = 1 // Reset if window passed - } - - if (newCount >= 5) { - blockedUntil = new Date(Date.now() + BLOCK_DURATION_MINUTES * 60 * 1000).toISOString() - } - - await supabase - .from('rate_limits') - .update({ - count: newCount, - last_attempt: new Date().toISOString(), - blocked_until: blockedUntil - }) - .eq('id', limit.id) - } else { - await supabase.from('rate_limits').insert({ - ip_address: ip, - action, - count: 1, - last_attempt: new Date().toISOString() - }) - } - - } catch (error) { - console.error('Rate limit increment failed:', error) - } -} diff --git a/src/lib/supabase/session.ts b/src/lib/supabase/session.ts index 3a4bc1b..f2f796d 100644 --- a/src/lib/supabase/session.ts +++ b/src/lib/supabase/session.ts @@ -36,22 +36,17 @@ export async function updateSession(request: NextRequest) { if ( !user && !request.nextUrl.pathname.startsWith('/login') && - !request.nextUrl.pathname.startsWith('/auth') + !request.nextUrl.pathname.startsWith('/auth') && + request.nextUrl.pathname !== '/' && + !request.nextUrl.pathname.startsWith('/galeri') ) { const url = request.nextUrl.clone() url.pathname = '/login' return NextResponse.redirect(url) } - // 2FA Enforcement - if (user && !request.nextUrl.pathname.startsWith('/verify')) { - const verifiedCookie = request.cookies.get('2fa_verified') - if (!verifiedCookie) { - const url = request.nextUrl.clone() - url.pathname = '/verify' - return NextResponse.redirect(url) - } - } + // 2FA Enforcement Removed + return supabaseResponse }