update
This commit is contained in:
190
inc/Hura8/System/Firewall.php
Normal file
190
inc/Hura8/System/Firewall.php
Normal file
@@ -0,0 +1,190 @@
|
||||
<?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];
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user