Files
admin_hura_8/inc/Hura8/System/Firewall.php
2024-01-29 10:39:53 +07:00

191 lines
5.4 KiB
PHP

<?php
// Simple firewall to protect DDOS attack
// It's better to use
// https://github.com/terrylinooo/shieldon
// https://github.com/antonioribeiro/firewall
// https://packagist.org/packages/middlewares/firewall
namespace Hura8\System;
use Jaybizzle\CrawlerDetect\CrawlerDetect;
class Firewall
{
const MAX_CONCURRENT_VISIT_ALLOW = 5;
const WHITE_LIST_IP = ROOT_DIR . "/config/system/white_list_ip.txt";
const BAN_IP_LIST = ROOT_DIR . "/config/build/ban_ip_list.txt";
const TEMPORARY_BAN_DURATION = 60; //seconds
/* @var $instance Firewall */
protected static $instance;
protected $log_dir = ROOT_DIR . '/cache/firewall/log/';
protected $ban_dir = ROOT_DIR . '/cache/firewall/ban/';
private $user_ip = '';
protected $white_list = [
// google bot ips
// bing bots
// others..
];
protected function __construct() {
// todo: use Redis for performance instead of directory
/*$this->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];
}
}