每当用户访问nginx服务器上的web时都会留下访问记录,这些记录被记录在access_log,可在配置文件中看到。

比如宝塔面板可直接在配置文件中看到:

 没有自定义的情况下这个文件打开是这样的:

 以下脚本实现,

1、可以自动执行,即使用命令行执行,每5分钟执行一次,已被注释

2、使用宝塔面板的计划任务定时访问地址,这样比较简单

3、分析最近的1万条记录,如果总条数小于700条,则跳出;如果最早和最小ip之间时间相隔小于12小时,则跳出;ip访问数大于300次,则符合条件,并与stopip文件中已保存的ip对比,如果stopip文件中没有,则添加至文件。

<?php
header('Content-type:text/html;charset=utf-8');

//打开日志文件
$ipstop_log_file_path='d:/BtSoft/wwwlogs/stopip_log.log';
if(file_exists($ipstop_log_file_path)){
    $ipstop_log = fopen($ipstop_log_file_path, "a+");//追加内容,w为覆盖内容
    fwrite($ipstop_log, "-----------开始执行:".date("Y/m/d h:i:s")."------------------\r\n");
    fwrite($ipstop_log, "本次执行会分析nginx_access_log中响应日志中最近10000条数据,在12小时内请求数大于700次的ip会被屏蔽\r\n");
}
/**
 * 取文件最后$n行
 * @param string $filename 文件路径
 * @param int $n 最后几行
 * @return mixed false表示有错误,成功则返回字符串
 */
function FileLastLines($filename,$n,$ipstop_log){
  if(!$fp=fopen($filename,'r')){
    echo "打开文件失败,请检查文件路径是否正确,路径和文件名不要包含中文";
    fwrite($ipstop_log, "打开文件失败,请检查文件路径是否正确,路径和文件名不要包含中文\r\n\r\n");
    fclose($ipstop_log);
    return false;
  }
  $pos=-2;
  $eof="";//是否一行的终止
  $first_time='';//第一个ip的请求时间
  $arr=array();
  $last_row_num=0;//读取的最大行数
  //当输入的行数大于0
  while($n>0){
    //没到一行的结束
    while($eof!="\n"){
      //打开的文件中定位
      if(!fseek($fp,$pos,SEEK_END)){
        //获取一个单一字符,看是否\n
        $eof=fgetc($fp);
        $pos--;
        //读取一行
        $now_row=fgets($fp);
        $ip=explode(' - ', $now_row)[0];
      }else{
        $n=0;
        $last_row_num--;
        break;
      }
    }
    //如果已有ip键名,则累加
    if(isset($arr[$ip])){
      $arr[$ip]++;
      $last_row_num++;
      $wz1=mb_strpos($now_row, '[');
      $wz2=mb_strpos($now_row, ']');
      $first_time=mb_substr($now_row, $wz1+1, $wz2-$wz1-1);
    }else if(!empty($ip) && strlen($ip)<16){
      $arr[$ip]=1;//否则新建键名
      $last_row_num++;
      $wz1=mb_strpos($now_row, '[');
      $wz2=mb_strpos($now_row, ']');
      $first_time=mb_substr($now_row, $wz1+1, $wz2-$wz1-1);
    }
    $eof="";
    $n--;
  }
  if($last_row_num<700){
   $ech_str="-----------由于少于700行,本次执行结束:".date("Y/m/d h:i:s")."------------------\r\n\r\n";
   echo $ech_str;//打印
   fwrite($ipstop_log, $ech_str);//写入log
   fclose($ipstop_log);
   return false;
  }else if(time()-strtotime($first_time)<43200){
   $ech_str="第一个ip请求的时间为".$first_time.",时间戳为".strtotime($first_time).",不足12小时,不具备分析条件,本次执行结束:".date("Y/m/d h:i:s")."------------------\r\n\r\n";
   echo $ech_str;//打印
   fwrite($ipstop_log, $ech_str);//写入log
   fclose($ipstop_log);
   return false;
  }else{
    $ech_str= "已分析:".$last_row_num."条数据, 第一个ip请求的时间为".$first_time."\r\n";
    echo $ech_str;//打印
    fwrite($ipstop_log, $ech_str);//写入log
    compara_ip($arr,$ipstop_log);
  }
}
//比较ip
function compara_ip($arr,$ipstop_log){
  //先读取频闭ip文件
  $file_path = "d:/BtSoft/wwwlogs/stopip.log";
  if(file_exists($file_path)){
    $stop_ip_str = file_get_contents($file_path);//将整个文件内容读入到一个字符串中
  }
  $str='';//要写入屏蔽ip的字符串
  //遍历数组
	foreach($arr as $key => $value){
	  //如果ip已经在屏蔽文件中,则不做处理
	  if($value && $value>300 && !strpos($stop_ip_str, $key)){
			$str.='deny '.$key.";\r\n";
		}
	}
	$myfile = fopen($file_path, "a+");//追加内容,w为覆盖内容
	fwrite($myfile, $str);
	fclose($myfile);
	$ech_str="-----------执行结束:".date("Y/m/d h:i:s")."------------------\r\n\r\n";
	echo $ech_str;//打印
    fwrite($ipstop_log, $ech_str);//写入log
    fclose($ipstop_log);
}

//手动定时执行
FileLastLines('d:/BtSoft/wwwlogs/waterwx.log',10000,$ipstop_log);

//自动定时执行, 每300s执行一次
// function executeEverySecond($function,$file_path, $num) {
//     while (true) {
//     echo "\r\n";
//     echo "\r\n";
//     echo "-----------开始执行:".date("Y/m/d h:i:s")."------------------\r\n";
//     echo "本次执行会分析nginx_access_log中响应日志中最近".$num."条数据,在12小时内请求数大于700次的ip会被屏蔽\r\n";
// 		$function($file_path,$num);
//       sleep(300);
//     }
// }

// executeEverySecond('FileLastLines','d:/BtSoft/wwwlogs/waterwx.weblf.cn.log',10000,$ipstop_log);

stopip:

最后在nginx配置文件中引入stopip.log

 执行日志:

 

 

更多推荐