diff --git a/public/api/contact.php b/public/api/contact.php index e2def2e..794b8b7 100644 --- a/public/api/contact.php +++ b/public/api/contact.php @@ -10,6 +10,25 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') { exit; } +// ─── Rate limiting (5 requests per minute per IP) ──────────────────────────── +$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] + ? explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0] + : ($_SERVER['REMOTE_ADDR'] ?? 'unknown'); +$ip = trim($ip); +$rate_file = sys_get_temp_dir() . '/rl_' . md5($ip) . '.json'; +$now = time(); +$window = 60; +$max_req = 5; +$rate_data = file_exists($rate_file) ? json_decode(file_get_contents($rate_file), true) : []; +$rate_data = array_filter($rate_data ?? [], fn($t) => $t > $now - $window); +if (count($rate_data) >= $max_req) { + http_response_code(429); + echo json_encode(['error' => 'Too many requests. Please try again later.']); + exit; +} +$rate_data[] = $now; +file_put_contents($rate_file, json_encode(array_values($rate_data))); + $data = json_decode(file_get_contents('php://input'), true); if (!$data) { http_response_code(400); @@ -28,6 +47,17 @@ if (!$name || !$message) { exit; } +// ─── Validation ─────────────────────────────────────────────────────────────── +if ($email && !filter_var($email, FILTER_VALIDATE_EMAIL)) { + http_response_code(400); + echo json_encode(['error' => 'Invalid email format']); + exit; +} +if (mb_strlen($name) > 100) { http_response_code(400); echo json_encode(['error' => 'Name too long']); exit; } +if (mb_strlen($email) > 254) { http_response_code(400); echo json_encode(['error' => 'Email too long']); exit; } +if (mb_strlen($phone) > 30) { http_response_code(400); echo json_encode(['error' => 'Phone too long']); exit; } +if (mb_strlen($message) > 5000) { http_response_code(400); echo json_encode(['error' => 'Message too long']); exit; } + // ─── Telegram (best-effort, not fatal) ─────────────────────────────────────── $BOT_TOKEN = '8138813013:AAElH2L5NspRLSdiFjDz6Qf32n4G24P_cj8'; $CHAT_ID = '-5230603582';