log_dir = ROOT_DIR . '/cache/firewall/log/'; $this->ban_dir = ROOT_DIR . '/cache/firewall/ban/'; if(!file_exists($this->log_dir)) { mkdir($this->log_dir, 0755, true); } if(!file_exists($this->ban_dir)) { mkdir($this->ban_dir, 0755, true); }*/ $this->user_ip = getIpAddress(); if(file_exists(self::WHITE_LIST_IP)) { $this->white_list = array_filter(explode("\n", file_get_contents(self::WHITE_LIST_IP))); } } public static function getInstance() { if(!static::$instance) { static::$instance = new self(); } return static::$instance; } // check if current user agent is an crawler public function isCrawler() : bool { $objCrawlerDetect = new CrawlerDetect(); //echo $objCrawlerDetect->getUserAgent(); // https://developers.google.com/search/docs/crawling-indexing/overview-google-crawlers return $objCrawlerDetect->isCrawler(); } public function monitor() { // todo: return false; $total_concurrent_visit = $this->logIp($this->user_ip); if($total_concurrent_visit > self::MAX_CONCURRENT_VISIT_ALLOW) { // ban if not in whitelist if(!$this->isWhiteListed()) { $this->banTemporary($this->user_ip, self::TEMPORARY_BAN_DURATION); } die("System overload"); } return true; } public function stopIfBanned() { if($this->isIPBanned()) { die("Perhaps, this website is not for you."); } return false; } protected function isWhiteListed() { $filter = new IPFilter($this->white_list); return $filter->check($this->user_ip); } protected function isIPBanned() { // check whitelist if($this->isWhiteListed()) { return false; } // check temporary ban /*$file_id = $this->ban_dir . $this->user_ip.'.data'; if(file_exists($file_id)) { $lift_time = file_get_contents($file_id); // lift_time is not over yet if($lift_time > time()) { return true; } }*/ // permanent ban $config_file = static::BAN_IP_LIST; if(@file_exists( $config_file )) { $list_ips = @file_get_contents( $config_file ); $list_ip_arr = array_filter(explode("\n", $list_ips)); $filter = new IPFilter($list_ip_arr); return $filter->check($this->user_ip); } return false; } // ban an IP temporarily protected function banTemporary($ip, $time) { $file_id = $this->ban_dir . $ip.'.data'; @file_put_contents($file_id, time() + $time); } // only allow 1 of these formats: // - Full IP: 127.0.0.1 // - Wildcard: 172.0.0.* // - Mask: 125.0.0.1/24 // - Range: 125.0.0.1-125.0.0.9 public static function validateIP($ip) { // - Full IP: 127.0.0.1 if (filter_var($ip, FILTER_VALIDATE_IP)) { return true; } // - Wildcard: 172.0.0.* if (strpos($ip, "*") !== false && preg_match("/^([0-9]+)\.([0-9]+)\.(([0-9]+)|\*)\.(([0-9]+)|\*)$/i", $ip)) { return true; } // - Mask: 125.0.0.1/24 if (strpos($ip, "/") !== false && preg_match("/^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)\/([0-9]+)$/i", $ip)) { return true; } // - Range: 125.0.0.1-125.0.0.9 if (strpos($ip, "-") !== false ) { list($begin, $end) = explode('-', $ip); return filter_var($begin, FILTER_VALIDATE_IP) && filter_var($end, FILTER_VALIDATE_IP); } return false; } private function logIp($ip) { $file_id = $this->log_dir . $ip.'.data'; $time_track = CURRENT_TIME - CURRENT_TIME % 5; // track within 5 seconds, $data = []; if(file_exists($file_id)) { $data = \json_decode(file_get_contents($file_id), true); } if(isset($data[$time_track])) { $data[$time_track] = $data[$time_track] + 1; }else{ $data[$time_track] = 1; } // only log for current time to prevent large $data's size // our purpose is only to detect if this IP is brutally forceing the system (too many visits per time) @file_put_contents($file_id, \json_encode($data)); return $data[$time_track]; } }