Jamers Posted January 6, 2015 Report Share Posted January 6, 2015 (edited) 我们平常能够使用到的防火墙大多是规则设置好后,然后就等人工分析相应日志后更新相应规则,这样就没有多少时效性,不能及时保证系统的安全。所以才萌发出搞个主动屏蔽的功能。 目前这套系统仅能够运行在启用ipfw的类*NIX或*BSD的系统中,当然PHP和mysql必不可少。另外程序只能够以root的身份在命令行状态进行执行,因为IPFW必须有root权限才能够操作。 系统原理为:定期执行相应程序分析指定日志文件(可以设置多个),从日志文件中挑出对系统不利的操作,一旦达到设定阀值后,即将指定IP临时屏蔽(按目前系统设定,那么接下来的24小时,此IP将无法访问本服务器),如果多次被屏蔽,将会被直接移至黑名单中,那样将永久屏蔽此IP的访问。具体可在config.php配置文件中修改。 以下帖出部分核心文件,完整的文件将在最下面提供下载。此程序仅供抛砖引玉,如果有更好的方案,可以一起探讨共同成长。如果此工具能够帮到一些朋友,我将不胜荣幸,如果有任何BUG之类的,也可以及时与我联系共同把此工具做好。 安装方式: 注意本程序仅供有足够unix操作经验的网友使用,权限方面的问题不多讲。 1. 首先没有conn.php,需要选执行一次 install.php,程序会自动生成conn.php 2. 待conn.php生成后,修改并创建相应mysql数据库及用户后再次运行install.php 3. 然后修改config.php,重要的是将各需要扫描的apache访问日志文件完整路径写上,原配置文件中的文件仅供测试使用 4. 修改crontab以root身份,每10到15分钟运行一下 php firewall.php ,这个时间可以根据自己的实际情况进行调整 5. 如果有需要加入白名单的IP地址以防止误封,可以手工在whitelist表中添加相应记录。 6. 如果事先有需要加入黑名单的IP地址也可以手工在blacklist表中添加相应记录 config.php <?php DEFINED('ROOT_DIR') || DEFINE('ROOT_DIR',dirname(__FILE__)); if (file_exists(ROOT_DIR.'/conn.php')) include_once(ROOT_DIR.'/conn.php'); class config { /** * 配置文件 Jamers 2015.1.5 */ static $dellogtime = -1; //删除数据时间 31*24*60*60 -1为不删除 static $blockvalue = 50; //屏蔽阀值,一天触发规则多少次 static $step = 1; //ID步行 static $white_start = 1100; //白名单起始ID static $white_set = 10; //白名单规则集 static $black_start = 1500; //黑名单起始ID static $black_set = 11; //黑名单规则集 static $temp_start = 10000; //临时屏蔽起始ID static $temp_set = 15; //临时屏蔽规则集 static $temp_durt = array(0 => 1,1 => 7,2 => -1); //临时屏蔽时间天数 -1为永久 static $ipfw = '/sbin/ipfw'; //IPFW完整路径,否则CRON无法执行 static $debug = false; //DEBUG标志 static $debug_out = 'debug.txt'; //DEBUG输出文件 static $list = array( 'data/bbs.zomew.net-access_log', ); //需要扫描的日志文件 static $filter = array( ' 404 ','%20and%20','%20or%20','/**/or/**/','phpinfo.php','act=phpinfo','phpmyadmin','%20union%20','select%20','><script>','=alert(', ); //需要过滤的内容 static $ignore = array( 'robots.txt','google=', ); //忽略列表数据,比如robots.txt //调用语句:cat 文件名 | grep -i "19/Dec/2014" | grep -i " 404 \|phpinfo.php" | grep -vi "robots.txt" } ?> cls_firewall.php 核心文件 <?php DEFINED('ROOT_DIR') || DEFINE('ROOT_DIR',dirname(__FILE__)); include_once(ROOT_DIR.'/config.php'); include_once(ROOT_DIR.'/cls_mysql.php'); class cls_firewall { /** * IPFW防火墙处理脚本,原设想自动根据apache日志屏蔽相应恶意访问IP * * Jamers 2015.1.5 */ public $vars=array(); public $DB; public $step = 1; private $dt,$ipfw,$debug,$debug_out; private $filter,$ignore; private $type = array('white','black','temp'); public $ids = array('white'=>1100,'black'=>1500,'temp'=>10000); public $sets = array('white'=>10,'black'=>11,'temp'=>15); public $qsql = array( 'white' => 'select ip from whitelist;', 'black' => 'select ip from blacklist;', 'temp' => 'select ip from blocked where stime+durt>=UNIX_TIMESTAMP()', ); public $output; //输出的语句 private $support; function __construct() { $this->step = config::$step; $this->ids['white'] = config::$white_start; $this->ids['black'] = config::$black_start; $this->ids['temp'] = config::$temp_start; $this->sets['white'] = config::$white_set; $this->sets['black'] = config::$black_set; $this->sets['temp'] = config::$temp_set; $this->dt = config::$dellogtime; $this->ipfw = config::$ipfw; $this->debug = false; $this->debug = config::$debug; $this->debug_out = config::$debug_out; $this->filter = implode('\\|',config::$filter); $this->ignore = implode('\\|',config::$ignore); $this->checksystem(); } private function checksystem() { $res = array(); $this->support = false; if (php_sapi_name()!='cli') return false; @exec('/usr/bin/uname',$res); //检测uname if ($this->debug) { $dd = var_export($res,true); $this->append($this->debug_out,'/usr/bin/uname'."\r\n".$dd."\r\n"); } if (count($res)>0) { $res = array(); @exec('/usr/bin/whoami',$res); //var_dump($res); if ($this->debug) { $dd = var_export($res,true); $this->append($this->debug_out,'/usr/bin/whoami'."\r\n".$dd."\r\n"); } if ($res[0]=='root') $this->support = true; } return $this->support; } function execute() { /** * 主调用程序,分析相应日志,并生成防火墙命令语句并执行 */ if (! $this->support) die('Your system is unable to support this program!this program need command line shell and root execute!'); //windows系统直接退出,不让玩! $this->loaddata(); //从日志文件中取得相应IP资料 //echo "1"; $this->delete_old_data(); //删除过期数据 //echo "2"; $t = $this->exec_filter(); //执行过滤语句 //var_dump($t); //echo date('Y-m-d H:i:s'); return date('Y-m-d H:i:s'); } function init_DB() { $this->DB = new cls_mysql($this->vars['dbhost'],$this->vars['dbuser'],$this->vars['dbpass'],$this->vars['dbname']); } private function checklist($ip) { /** * 检查是否在黑白名单中,在的话返回True */ $res = false; $sql = "select count(*) from ((select DISTINCT ip from whitelist) union (select DISTINCT ip from blacklist)) a where ip='{$ip}';"; $rs = $this->DB->getOne($sql,MYSQL_NUM); if (intval($rs[0])>0) $res = true; return $res; } private function loaddata() { /** * 读取日志数据 */ foreach (config::$list as $v) { $res = array(); $dstr = date('d/M/Y'); //$dstr = '19/Dec/2014'; //DEBUG专用 $p = '/^\s*(\d+)\s+([\d\.]+)$/'; $cmd = "/bin/cat {$v} | /usr/bin/grep -i '{$dstr}' | /usr/bin/grep -i '{$this->filter}' | /usr/bin/grep -vi '{$this->ignore}' | /usr/bin/awk '{print \$1}' | /usr/bin/sort | /usr/bin/uniq -c | /usr/bin/sort -nr "; if ($this->debug) $this->append($this->debug_out,$cmd); @exec($cmd,$res); if ($this->debug) { $dd = var_export($res,true); $this->append($this->debug_out,"\r\n".$dd."\r\n"); } foreach ($res as $v) { preg_match($p,$v,$t); $cc = 0; if (intval($t[1])>=config::$blockvalue) { //超过阀值,加入数据库,加入前先检查是否已有数据 //如果在黑名单中,应该就不会出现在这里了吧? $ip = trim($t[2]); $sql = "select count(id) from blocked where ip='{$ip}' and stime>UNIX_TIMESTAMP(DATE_FORMAT(now(),'%Y-%m-%d 0:0:0'))"; $rs = $this->DB->getOne($sql,MYSQL_NUM); if ($rs[0]<=0) { //当天没数据,加! if (! $this->checklist($ip)) { //检测是否在黑白名单中 $max = max(config::$temp_durt); $sql = "select count(id) from blocked where ip='{$ip}' and stime<UNIX_TIMESTAMP(DATE_FORMAT(now(),'%Y-%m-%d 0:0:0')) and stime>=UNIX_TIMESTAMP(DATE_FORMAT(DATE_SUB(now(),INTERVAL {$max} DAY),'%Y-%m-%d 0:0:0'))"; //N天内 $rs = array(); $rs = $this->DB->getOne($sql,MYSQL_NUM); if ($rs[0]>=0) { $cc = intval($rs[0]); } $ary = array('ip'=>$ip,'stime'=>time()); $ary['durt'] = config::$temp_durt[$cc]; if ($ary['durt']==-1) { $this->DB->do_insert('blacklist',array('ip'=>$ip,'addtime'=>time())); }else{ $ary['durt'] *= 24*60*60; $this->DB->do_insert('blocked',$ary); } } } } } } } private function delete_old_data() { /** * 删除过期数据 原设置为31天 * * @var mixed */ if ($this->dt >=0) { $sql = "delete from blocked where stime+durt+{$this->dt} < UNIX_TIMESTAMP();"; //echo $sql; $this->DB->execute($sql); } } private function buildall() { /** * 从数据库取所有IP数据,返回IPFW语句 * */ $res = ''; foreach ($this->type as $v) { $res .= "{$this->ipfw} delete set {$this->sets[$v]};"; $res .= $this->buildrule($v); } $this->output = $res; return $res; } private function exec_filter($len=1024) { //echo 'here!'; if ($this->output=='') $this->buildall(); if ($this->output=='') return 'NULL'; //var_dump($this->output); $str = $this->output; $res = array(); while (strlen($str)>0) { if (strlen($str)<=$len) { $res[] = $str; @exec($str); $str = ''; }else{ $tmp = substr($str,0,$len); $pos =strrpos($tmp,';'); $out = substr($tmp,0,$pos+1); $res[] = $out; @exec($out); $str = substr($str,$pos+1); } } if ($this->debug) { $dd = var_export($res,true); $this->append($this->debug_out,"exec_filter\r\n".$dd."\r\n"); } return $res; } private function buildrule($type) { /** * 生成相应ipfw语句 * * Jamers 2015.1.5 */ $res = ''; if (isset($this->qsql[$type])) { $sql = $this->qsql[$type]; $rs = $this->DB->getAll($sql,MYSQL_NUM); if ($rs) { foreach ($rs as $v) { $opt = strtolower($type)=='white'?'allow':'deny'; $res .= "{$this->ipfw} -q add {$this->ids[$type]} set {$this->sets[$type]} {$opt} ip from {$v[0]} to me;"; $this->ids[$type]+=$this->step; } return $res; }else{ return ''; } }else{ return ''; } } private function append($file,$str) { $fp = fopen($file,'a'); fwrite($fp,$str); fclose($fp); } } ?> firewall.php 调用主程序 <?php DEFINED('ROOT_DIR') || DEFINE('ROOT_DIR',dirname(__FILE__)); include_once(ROOT_DIR.'/cls_firewall.php'); $fw = new cls_firewall(); if (isset($INFO)) { $fw->vars = $INFO; $fw->init_DB(); }else{ die('system config error!'); } echo $fw->execute(); ?> firewall.rar Edited January 7, 2015 by Jamers 经过调试,修正部分问题。 Link to comment Share on other sites More sharing options...
Jamers Posted January 6, 2015 Author Report Share Posted January 6, 2015 已在服务器上实施,静待测试结果。 Link to comment Share on other sites More sharing options...
Jamers Posted January 7, 2015 Author Report Share Posted January 7, 2015 经过两天的测试,发现一些问题,正常shell内执行的时候一切正常,但是放到crontab中执行老是反应,后经过调试,发现由于未指定完全路径,导致文件无法找到,重新修正相应文件,将原帖内容和附件一同修改。 今后开发系统内直接执行的脚本时必须使用绝对路径。。谨记! Link to comment Share on other sites More sharing options...
Recommended Posts
Please sign in to comment
You will be able to leave a comment after signing in
Sign In Now