rate_limit_file = __DIR__ . '/../cache/rate_limits.json'; if (!file_exists($this->rate_limit_file)) { if (!is_dir(dirname($this->rate_limit_file))) { mkdir(dirname($this->rate_limit_file), 0755, true); } file_put_contents($this->rate_limit_file, '{}'); } } public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } public static function sanitizeInput($data) { if (is_array($data)) { return array_map([self::class, 'sanitizeInput'], $data); } return htmlspecialchars(strip_tags(trim($data)), ENT_QUOTES, 'UTF-8'); } public static function validateRecaptcha($response) { $url = 'https://www.google.com/recaptcha/api/siteverify'; $data = [ 'secret' => RECAPTCHA_SECRET_KEY, 'response' => $response ]; $options = [ 'http' => [ 'header' => "Content-type: application/x-www-form-urlencoded\r\n", 'method' => 'POST', 'content' => http_build_query($data) ] ]; $context = stream_context_create($options); $result = file_get_contents($url, false, $context); $result_json = json_decode($result, true); return isset($result_json['success']) && $result_json['success'] === true; } public function checkRateLimit($ip, $endpoint, $limit = 60, $window = 3600) { $limits = json_decode(file_get_contents($this->rate_limit_file), true); $now = time(); $key = $ip . ':' . $endpoint; if (!isset($limits[$key])) { $limits[$key] = ['count' => 0, 'window_start' => $now]; } if ($now - $limits[$key]['window_start'] > $window) { $limits[$key] = ['count' => 0, 'window_start' => $now]; } $limits[$key]['count']++; file_put_contents($this->rate_limit_file, json_encode($limits)); return $limits[$key]['count'] <= $limit; } public static function generateCSRFToken() { if (!isset($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } return $_SESSION['csrf_token']; } public static function verifyCSRFToken($token) { return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token); } public static function encrypt($data) { $iv = random_bytes(16); $encrypted = openssl_encrypt( $data, 'AES-256-CBC', self::$encryptionKey, 0, $iv ); return base64_encode($iv . $encrypted); } public static function decrypt($data) { $data = base64_decode($data); $iv = substr($data, 0, 16); $encrypted = substr($data, 16); return openssl_decrypt( $encrypted, 'AES-256-CBC', self::$encryptionKey, 0, $iv ); } public static function hashPassword($password) { return password_hash($password, PASSWORD_DEFAULT); } public static function verifyPassword($password, $hash) { return password_verify($password, $hash); } }