Linux 网站访问日志分析脚本
功能概述
该 PHP 脚本用于免入库直接分析 .log
文件,特别是 Linux 网站访问日志文件。它通过解析日志文件,统计各类数据,例如 IP 访问量、状态码分布、返回内容大小、User-Agent 信息、访问路径、访问时间等,帮助用户分析潜在的安全威胁或异常行为。
核心功能
日志文件读取与解析
- 使用
fgetcsv
逐行读取日志文件,提取关键字段进行分析。
- 使用
IP 访问量排序
- 统计每个 IP 的访问次数,分析高频访问 IP。
状态码分析
- 统计各 IP 对不同状态码的请求次数,识别异常状态(如 404、503)。
返回内容大小分析
- 分析每个 IP 的返回内容大小,识别可能的大文件下载行为。
User-Agent 分析
- 通过 User-Agent 信息识别爬虫、攻击行为等。
访问路径分析
- 统计访问的路径,识别潜在的攻击路径或异常访问。
访问时间分析
- 分析访问集中时段,帮助定位高频访问发生的具体时间。
优化后的代码
<?php
//免入库直接分析.log linux网站访问日志文件
//宝塔下LNMP 7.1开发;请linux nginx PHP5.5-7.3 使用
header("Content-type:text/html;charset=utf-8");
//ob_implicit_flush();
ini_set('memory_limit', '-1'); //-1不限制内存
set_time_limit(0); //0不限制超时时间
$lie0 = "0"; //第几-1列为IP
$lie1 = "6"; //第几-1列为状态码
$lie2 = "7"; //第几-1列为返回内容长度
$lie3 = "9"; //第几-1列为客户端UA
$lie4 = "3"; //第几-1列为时间
$lie5 = "5"; //第几-1列为访问路径
$paths = "./logs/";
/*
宝塔下LNMP 7.1开发;请linux nginx PHP5.5-7.3 使用
免入库直接分析.log linux网站访问日志文件
1. 按IP访问量排序闲时:分析各IP总访问量(便于分析高频访问用户)
2. 显示各IP访问的各反馈状态的数量(降序)用于分析
2.1 404多则可能在破解你后台或者备份文件
2.2 503多则可能发生过载访问...
3. 显示各IP访问的回馈页面大小(访问量降序)分析可能大文件被下载...
4. 显示各IP访问的UA信息(访问量降序),可以分析是否蜘蛛/爬虫/UA攻击等...
5. 显示各IP访问的PAGE页面(访问量降序),可以分析是否自己网站页面
不是则破解后台/get攻击/破解访问/对高消页面的攻击...
6. 显示各IP访问的访问时段(访问量降序):分析高频访问发生时段快速查找对应日志
7. 辅助分析你页面其他维度
*/
$sty = "<style>table{border-top:2px solid #0180CF; margin:0 auto;font-size:12px;width:99%;}
table td{min-width:77px;max-width:300px;border-bottom:1px solid #a2c6d3;padding:5px 0px;word-wrap:break-word;word-break:break-all;}
table tr:nth-child(even){background: #FCFCFC;}
.tt{background:#e5f2fa;line-height:18px;FONT-SIZE:12px;font-weight:600;}
textarea,select{display:block;width;99%;}
</style>";
function readcsv($filex){
$handle = fopen($filex, "r");
while ($data = fgetcsv($handle,0," ")) {
yield $data; //php 5.5+
}
fclose($handle);
}
function logF($arr,$fie){
global $yms;
$txt = "\r\n\r\n".count($arr)."种/共".array_sum($arr);
arsort($arr); //键值降序
foreach ($arr as $ti => $tis) {
if(is_array($tis)) $tis = join("|",$tis);
$txt .= "\r\n$tis\t$ti";
}
if (!file_exists("./$yms/")){ mkdir("./$yms/",0777,true); }
file_put_contents("./$yms/$fie", $txt, FILE_APPEND);
}
function readarr($arr){
$txt = "<h5>".count($arr)."种 /共".array_sum($arr)."</h5>";
arsort($arr); //键值降序
foreach ($arr as $ti => $tis) {
if(is_array($tis)) $tis = join("|",$tis);
$txt .= "<p>$ti => $tis</p>\r\n";
}
return $txt;
}
function topuaa($arr,$uax,$tims="20"){
$txts = count($arr); $txt = "<h5>$txts 种/共".array_sum($arr)."</h5>";
$iy = 0; arsort($arr);
foreach ($arr as $ti => $tis) {
$gj = isgong($uax[$ti]);
$txt .= "<p>$tis => $gj => {$uax[$ti]}</p>\r\n";
$iy++; if($iy>$tims) return $txt;
}
return "".$txt;
}
function topupg($arr,$uax,$tims="20"){
$txts = count($arr); $txt = "<h5>$txts 种/共".array_sum($arr)."</h5>";
$iy = 0; arsort($arr);
foreach ($arr as $ti => $tis) {
$txt .= "<p>$tis => {$uax[$ti]}</p>\r\n";
$iy++; if($iy>$tims) return $txt;
}
return "".$txt;
}
function topups($arr,$uax,$tims="20"){
$txts = count($arr); $txt = "<h5>$txts 种/共".array_sum($arr)."</h5>";
$iy = 0; arsort($arr);
foreach ($arr as $ti => $tis) {
$txt .= "<p>$ti => $tis</p>\r\n";
$iy++; if($iy>$tims) return $txt;
}
return "".$txt;
}
function topvis($arr,$sizs="20"){
$txts = count($arr); $txt = "<h5>$txts 种/共".array_sum($arr)."</h5>";
$iy = 0; krsort($arr);
foreach ($arr as $ti => $tis) {
$txt .= "<p>$ti => $tis</p>\r\n";
$iy++; if($iy>$sizs) return $txt;
}
return $txt;
}
function isgong($txts){
if($txts=="" || $txts=="-"){
$zt = "异常空UA";
}elseif(preg_match_all('/(\\\x[a-zA-Z0-9_]{1,4}){2,4}/', $txts)){
$zt = "异常攻击";
}elseif(preg_match_all('/(spider|bot|crawler|robot)/i', $txts)){
$zt = "异常蜘蛛";
}elseif(preg_match_all('/(curl|requests|robot|python|urllib3|pantest)/i', $txts)){
$zt = "异常爬虫";
}elseif(preg_match_all('/(ALittle|Dalvik|is_mobile|Go-http-client|apache-HttpClient|Fuzz)/i', $txts)){
$zt = "异常爬虫";
}elseif(preg_match("/\@([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61})?\.)+[a-zA-Z]{2,8}/i", $txts)){
$zt = "异常爬虫邮";
}elseif(preg_match_all('/(http|https|ftp)/i', $txts) && preg_match("/([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61})?\.)+[a-zA-Z]{2,8}/i", $txts)){
$zt = "异常爬虫网";
}else{
$zt = "普通";
}
return $zt;
}
$n = (isset($_POST['n']))?addslashes($_POST['n']):"n"; //日志名称
if($n!="n"){
if(!stristr($n."@",".log@")) exit("必选 .log文件");
$start = microtime(true);
$mstar = memory_get_usage();
$filex= $paths.$n; //修改linux日志文件路径
if(!file_exists($filex)) exit("$logf 不存在");
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $n . '.html"');
//header('Content-Length: ' . filesize($dadirs));
echo $sty;
echo "分析{$n}高频访问IP,404页面多,大文件疑似下载,蜘蛛否等";
$ii=0; $iz=0; $sqla = array(); $sqlb = array(); $sqlc = array(); $sqlh = array();
$sqld = array(); $sqle = array(); $sqlf = array(); $sqlg = array();
foreach (readcsv($filex) as $key => $kar) {
$ii++;
//公共:IP总次数
$ipx1 = $kar[$lie0];
if(!$sqla[$ipx1]){ $sqla[$ipx1] = 1; }else{ $sqla[$ipx1] += 1;}
//公共:状态分析用于分析是否在破后台网址或下载.zip备份路径等
$ipx2 = $kar[$lie1]; //STATS;200/404...
if(!$sqlb[$ipx1][$ipx2]){ $sqlb[$ipx1][$ipx2] = 1; }else{ $sqlb[$ipx1][$ipx2] += 1;}
//公共:反馈页面大小用于显示可能的大文件下载
$ipx3 = $kar[$lie2];
if(!$sqlc[$ipx1][$ipx3]){ $sqlc[$ipx1][$ipx3] = 1; }else{ $sqlc[$ipx1][$ipx3] += 1;}
//公共:UA客户端用于分析用户是否蜘蛛等
$ipx4 = md5($kar[$lie3]);
if(!$sqld[$ipx1][$ipx4]){ $sqld[$ipx1][$ipx4] = 1; $sqle[$ipx4]=$kar[$lie3]; }else{ $sqld[$ipx1][$ipx4] += 1;}
//公共:访问页面用于分析攻击否等
$ipx5 = md5($kar[$lie5]);
if(!$sqlg[$ipx1][$ipx5]){ $sqlg[$ipx1][$ipx5] = 1; $sqlf[$ipx5]=$kar[$lie5]; }else{ $sqlg[$ipx1][$ipx5] += 1;}
//公共:访问时段用于分析攻击集中时段
$ipx61 = Trim($kar[$lie4],"["); $ipx62 = explode(":",$ipx61);
$ipx6 = $ipx62[0]."_H".$ipx62[1]; //$ipx62[0]年月日[1]时[2]分
if(!$sqlh[$ipx1][$ipx6]){ $sqlh[$ipx1][$ipx6] = 1; }else{ $sqlh[$ipx1][$ipx6] += 1;}
}
arsort($sqla);
$tix = explode("|", "IP|次数|状态|回馈|UA样|PAGE|时段");
echo "<table>";
echo "<tr class='tt'><td width='80'>".join("</td><td>",$tix)."</td></tr>";
foreach ($sqla as $tip => $tcs) {
echo "<tr><td>@$tip</td><td>$tcs</td><td>".readarr($sqlb[$tip])."</td><td>".topvis($sqlc[$tip])."</td><td>".topuaa($sqld[$tip],$sqle,"50")."</td><td>".topupg($sqlg[$tip],$sqlf,20)."</td><td>".topups($sqlh[$tip],"",30)."</td></tr>";
}
echo "</table>";
$sstop = microtime(true); $mstop = memory_get_usage();
$fsize = number_format(filesize($filex)/1024/1024,2);
$us = "<p>分析{$fsize}MB/{$ii}条日志@耗时".number_format($sstop-$start, 2);
$us .= "@耗内存".number_format(($mstop-$mstar)/1024, 2)."KB";
$us .= "/总".number_format(memory_get_peak_usage()/1024,2).'KB</p>';
echo $us;
}else{
$dd = ""; echo $sty;
echo '<form name="q" method="post" action="?w=fee">';
echo '<h3>注意:高耗资源,请闲时操作</h3>放.log文件到logs文件夹下后选择分析';
echo '<p>1核1G配置:分析170MB耗时12秒/50M内存左右!大部分主机可能有限定内存和相应时间。</p>';
echo "<select name=\"n\" id=\"n\" class=\"time\" >";
echo "<option value=\"\">请选择.log文件</option>\r\n";
$handler = opendir($paths); //打开当前文件夹由$paths指定。
$fileArr = array();
while (($fname = readdir($handler)) !== false) {
if ($fname != "." && $fname != "..") { //文件夹文件
if (is_dir($paths . "" . $fname)) { //如果是文件夹
} else {
$fTime = filemtime($paths . "" . $fname);
$fileArr[$fname] = $fTime;
}
}
}
arsort($fileArr);
foreach ($fileArr as $fname => $ftime) {
if(stristr($fname."@",".log@")){echo "<option value=\"$fname\">$fname</option>\r\n";}
}
@closedir($paths);
echo "</select>";
echo "<br><input type=\"submit\" name=\"button\" value=\"提交查询\" />";
}
echo '</form>';
总结
该脚本适合在 Linux 环境下直接分析 Nginx 访问日志,具有高效、直观的特点。通过该工具,管理员可以快速分析网站访问日志,检测潜在安全威胁。