'; $kill_history['20180919'] = 'cookie_destroy內刪除上層domain的cookie'; $kill_history['20180919'] = 'kill_log增加紀錄觸動kill的來源key'; $kill_history['20180829'] = '第一次的DB連線$link_id改用kill_config.php內的連線設定(因為有些機器本機沒有安裝mysql)'; $kill_history['20180816'] = 'kill_log增加紀錄觸動kill的來源'; $kill_history['20180613'] = '避免GET參數不斷重複造成網站的網頁數無限增加'; $kill_history['20180511'] = '新增cookie_destroy()'; $kill_history['20160922'] = '判斷$dirty_word_pattern不存在時,不執行檢查攻擊字串,避免警告訊息'; $kill_history['20160818'] = 'level 3 不偵測攻擊字串'; $kill_history['20160217'] = 'htmlspecialchars( $str, ENT_QUOTES|ENT_HTML401, "UTF-8" ) 填上$glag'; $kill_history['20160217'] = 'mysqli_real_escap_string需要預先做mysql連線'; $kill_history['20151012'] = '危險字串新增evel,誤解字串新增level'; $kill_history['20150324'] = '新增cookie HTTP_ONLY設定'; $kill_history['20121117'] = '新增$safe_word變數:/script/'; $kill_history['20121117'] = '新增from_facebook函數,允許fascebook的連線'; $kill_history['20120426'] = '1.不檢查$_SESSION。'."\n".'2.對各別參數檢查長度(800)'; $kill_history['20111003'] = '用正規表示式檢查危險字串'; $kill_history['20110930'] = '1.將kill拆成兩部分,分出kill_config.php。'."\n".'2.把$_GET,$_POST,$HTTP_GET_VARS,$HTTP_POST_VARS給unset掉,只對$_REQUEST和$_SESSION做replace_htmlspecialchars。'."\n".'3.改寫CheckCrossSiteScripting,獨立$dirty_word設定,新增$safe_word變數。'."\n".'4.阻擋事件發生時,連結導向改為該$_SERVER["HTTP_HOST"],並設定charset為utf-8。'; $kill_history['20110916'] = '危險字串新增%25,%23,%26'; if( isset($_GET['kill_version']) ){ show_history($kill_history); exit; } //目前正在檢查的key $currentKey = ''; //記錄檔 $kill_log = '/home/web/log/kill_log'; //20160217 for mysqli_real_escap_string() by windgod, 20180829用kill_config的連線設定 if( !$link_id = mysqli_connect($firstDBLink['host'],$firstDBLink['user'],$firstDBLink['pass']) ){ echo "firstDBLink[host] connect error:".$firstDBLink['host']; exit; } unset($firstDBLink); //危險字串 $dirty_word_level[1] = array( 'script','alert','.location','href','eval', 'select','schema','count', '<','>','%3C','%3E', '"',"'",'%27','%22', '%25','%23','%26', '%0d','%0a', ); $dirty_word_level[2] = array( 'script','alert','.location','href','eval', 'select','schema','count', '<','>','%3C','%3E', '%25','%23','%26', '%0d','%0a', ); $dirty_word_level[3] = array( ); $dirty_word_level[9] = array(); //不偵測XSS if( is_array($dirty_word_level[$kill_level]) && count($dirty_word_level[$kill_level])>0 ) $dirty_word_pattern = make_pattern_str( $dirty_word_level[$kill_level] ); //會被誤解的字串 $safe_word[] = 'description'; $safe_word[] = 'account'; $safe_word[] = 'ccount:'; $safe_word[] = 'country'; $safe_word[] = 'county'; $safe_word[] = ', text/javascript'; $safe_word[] = 'selected'; $safe_word[] = 'count(*) as'; $safe_word[] = '/script/'; $safe_word[] = 'counter'; $safe_word[] = 'getSelectId'; $safe_word[] = ''; $safe_word[] = 'hitcount'; $safe_word[] = 'countie'; $safe_word[] = 'location_city1'; $safe_word[] = 'location_city2'; $safe_word_pattern = make_pattern_str( $safe_word ); ini_set("session.cookie_httponly",1); if(!session_id()) session_start(); //檢查最後面加/ if( isset($_SERVER['PATH_INFO']) and $_SERVER['PATH_INFO'] != '' ){ session_destroy(); cookie_destroy(); exec('echo "======== '.date('Ymd His').' =========" >> '.$kill_log); exec('echo "FILE => '.commend_injection_anti($_SERVER['PHP_SELF']).'" >> '.$kill_log); exec('echo "path_info =>'.$_SERVER["PATH_INFO"].'" >> '.$kill_log); exec('echo "" >> '.$kill_log); header('HTTP/1.0 400 Bad Request'); echo ''; kill_msg('$_SERVER["PATH_INFO"]有問題,可能是url裡的php檔名後面多了/'); echo ''; echo "此網頁不存在,將於5秒後自動換頁!"; exit; } //先重新產生$_SERVER內的 QUERY_STRING 與 REQUEST_URI,為了避免GET參數無限重複,造成網站網頁數無限增加 20180613 by windgod $tmpURI = array(); foreach( $_GET as $k=>$v ){ $tmpURI[] = urlencode($k).'='.urlencode($v); } unset($k,$v); if( count($tmpURI) > 0 ){ $tmpURI = implode('&',$tmpURI); $_SERVER['QUERY_STRING'] = $tmpURI; $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'].'?'.$tmpURI; } //將不需要的cookie值刪除 20200715 by windgod unset( $_SERVER['HTTP_COOKIE'] ); $parentDomain = substr( $_SERVER['HTTP_HOST'], strpos($_SERVER['HTTP_HOST'],'.') ); $save_cookie = [ '_ga', '_gat', '_gid', 'PHPSESSID' ]; if(is_array($_COOKIE)) foreach( $_COOKIE as $k=>$v ){ if( !in_array($k,$save_cookie) ){ setcookie( $k, "", time()-86400); setcookie( $k, "", time()-86400, '/', $parentDomain); } } //轉換字串與檢查CrossSite危險字 unset($HTTP_GET_VARS,$HTTP_POST_VARS,$_POST,$_GET); unset($_SERVER['SERVER_SIGNATURE'],$_SERVER['HTTP_AMP_CACHE_TRANSFORM'],$_SERVER['HTTP_IF_NONE_MATCH']); if( $kill_level == '3' ){ CheckCrossSiteScripting($_REQUEST,'REQUEST'); CheckCrossSiteScripting($_COOKIE,'COOKIE'); //CheckCrossSiteScripting($_SESSION); }else{ replace_htmlspecialchars($_REQUEST,'REQUEST'); replace_htmlspecialchars($_COOKIE,'COOKIE'); //replace_htmlspecialchars($_SESSION); } //$_SERVER僅檢查CrossSite危險字,不轉換字串 if( isset($_SERVER['HTTP_REFERER']) ) $_SERVER['HTTP_REFERER'] = str_replace('%26','&',$_SERVER['HTTP_REFERER']); //應付facebook的導向 //只檢查指定的$_SERVER變數 20200911 by windgod $save_server = ['USER', 'HOME', 'SCRIPT_NAME', 'REQUEST_URI', 'QUERY_STRING', 'REQUEST_METHOD', 'SCRIPT_FILENAME', 'CONTEXT_DOCUMENT_ROOT', 'DOCUMENT_ROOT', 'REQUEST_SCHEME', 'SERVER_PORT', 'SERVER_ADDR', 'SERVER_NAME', 'HTTP_ACCEPT', 'HTTP_USER_AGENT', 'HTTP_HOST', 'UNIQUE_ID', 'PHP_SELF', 'REQUEST_TIME_FLOAT', 'REQUEST_TIME', 'HTTP_REFERER', 'REMOTE_ADDR', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED' ]; if(is_array($save_server)) foreach( $save_server as $v ){ if( isset($_SERVER[$v]) ){ CheckCrossSiteScripting($_SERVER[$v],'SERVER'); } } function replace_htmlspecialchars( &$value ,$source=''){ $value = fix_XSS_code($value,$source); } function fix_XSS_code($check_value,$source=''){ global $currentKey; if (is_array($check_value)){ foreach( $check_value as $k => $v ){ $currentKey = $k; $check_value[$k]= fix_XSS_code($v,$source); } }elseif( trim($check_value) != '' ){ $check_value= htmlspecialchars( $check_value, ENT_QUOTES|ENT_HTML401, "UTF-8" ); CheckCrossSiteScripting($check_value,$source); } return $check_value; } function CheckCrossSiteScripting($strData,$source=''){ global $dirty_word_pattern,$safe_word_pattern,$kill_level,$kill_log,$currentKey; if( is_array($strData) ){ foreach( $strData as $k=>$v ){ $currentKey = $k; CheckCrossSiteScripting($v,$source); } }else{ if( $kill_level != 3 && strlen($strData) > 2000){ exec('echo "======== '.date('Ymd His').' =========" >> '.$kill_log); exec('echo "IP => '.commend_injection_anti($_SERVER['REMOTE_ADDR']).'" >> '.$kill_log); exec('echo "FILE => '.commend_injection_anti($_SERVER['DOCUMENT_ROOT'].$_SERVER['PHP_SELF']).'" >> '.$kill_log); exec('echo "DATA => '.commend_injection_anti($strData).'" >> '.$kill_log); exec('echo "超過2000字" >> '.$kill_log); exec('echo "" >> '.$kill_log); header('HTTP/1.0 400 Bad Request'); echo ''; echo "Invalid Request !!\n
\n"; echo "您輸入的參數無效,將於5秒後自動換頁!"; kill_msg('('.$source.')參數長度超過2000('.strlen($strData).')'); exit(); } $exclude_safe_word = preg_replace( $safe_word_pattern,'', $strData ); if( $dirty_word_pattern ){ preg_match_all( $dirty_word_pattern, $exclude_safe_word, $matches ); if( count($matches[0]) > 0 ){ exec('echo "======== '.date('Ymd His').' =========" >> '.$kill_log); exec('echo "IP => '.commend_injection_anti($_SERVER['REMOTE_ADDR']).'" >> '.$kill_log); exec('echo "FILE => '.commend_injection_anti($_SERVER['DOCUMENT_ROOT'].$_SERVER['PHP_SELF']).'" >> '.$kill_log); exec('echo "DATA => '.commend_injection_anti($strData).'" >> '.$kill_log); exec('echo "safe_word =>'.$exclude_safe_word.'" >> '.$kill_log); exec('echo "受測字串 =>'.$exclude_safe_word.'" >> '.$kill_log); exec('echo "matches =>'.implode(',',$matches[0]).'" >> '.$kill_log); exec('echo "source =>'.$source.'['.$currentKey.']" >> '.$kill_log); exec('echo "" >> '.$kill_log); session_destroy(); cookie_destroy(); header('HTTP/1.0 400 Bad Request'); echo ''; kill_msg('('.$source.')'.$matches); echo ''; echo "Invalid Request !!\n
\n"; echo "您輸入的參數無效,將於5秒後自動換頁!"; exit(); } } } } function trance_HTML_ENTITIES( $str ){ $trans = get_html_translation_table (HTML_ENTITIES); //$encoded = strtr ($str, $trans); $trans = array_flip ($trans); return strtr($str, $trans); } function sql_injection_anti( $str ){ global $kill_level; if( sprintf("%d",$str) == $str ){ return $str; } if( $kill_level == '3' ){ //沒有轉換字串,所以不用轉回去 $trance_str = $str; }else{ $trance_str = trance_HTML_ENTITIES($str); $trance_str = str_replace(array('\\"',"\\'"),array('"',"'"),$trance_str); } $new_str = mysqli_real_escape_string($GLOBALS['link_id'],$trance_str); return $new_str; } function commend_injection_anti( $str ){ if( sprintf("%d",$str) == $str ){ return $str; } $trance_str = trance_HTML_ENTITIES($str); $new_str = escapeshellcmd($trance_str); return $new_str; } function kill_msg($msg){ global $show_kill_msg,$kill_level; if( $show_kill_msg === true ){ echo "kill level:$kill_level \n
\n"; print_r($msg);exit; } } function show_history($kill_history){ echo ' kill_XSS.php 歷史紀錄 '; foreach( $kill_history as $date=>$info ){ $info = htmlentities($info); echo ''; } echo '
kill_XSS.php 歷史紀錄
日期 更新內容
'.$date.' '.nl2br($info).'
'; } function make_pattern_str( $pattern_data ){ $pattern_wall = '_____'; $temp1 = implode($pattern_wall,$pattern_data); $pattern[] = '/(\\\\)/ix'; $pattern[] = '/(\ )/'; $pattern[] = '/(\(|\)|\/|\$|\"|\^|\*|\+|\[|\]|\|)/i'; $replacement[] = '\\\\\\\\\\\\$1'; $replacement[] = '\\s'; $replacement[] = '\\\\$1'; $pattern_str = preg_replace( '/'.$pattern_wall.'/','|', preg_replace( $pattern, $replacement, $temp1 ) ); return '/'.$pattern_str.'/ix'; } //刪除cookie function cookie_destroy(){ $parentDomain = substr( $_SERVER['HTTP_HOST'], strpos($_SERVER['HTTP_HOST'],'.') ); if( is_array($_COOKIE) ) foreach( $_COOKIE as $k=>$v ){ setcookie( $k, "", time()-86400); setcookie( $k, "", time()-86400, '/', $parentDomain); //20180919 } } //記錄debug動作 function write_log( $msg ){ $ini = array( '\[ ok \]', '\[fail\]', '\[ msg \]', '\[show\]', "\n" ); $new = array( "[ ok ]\t" , "[fail]\t" , "[ msg ]\t" , "[show]\t", '' ); $msg = str_replace( $ini, $new, $msg ); $file = '/home/web/log/SYSTEM_msg.admin.'.date('Ym'); $fd = fopen ($file, "a+"); fwrite( $fd , "\t".date("Y-m-d H:i:s")."\t".$_SERVER['REMOTE_ADDR']."\t".$_SERVER['PHP_SELF']."\n" ); fwrite( $fd , $msg."\n" ); fwrite( $fd , "\n" ); fclose( $fd ); } 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36', //瀏覽器資訊 'MAXREDIRS'=>5, //redirect最大次數限制 'CONNECTTIMEOUT'=>15,//連線的timeout時間(秒) 'TIMEOUT'=>15, //timeout時間(秒) 'NOBODY'=>false, //不下載內容 'HEADER'=>false, //下載HEADER 'POST_DATA'=>array(), //POST的資料 'POST_BY_STR'=>'N', // POST的陣列先用http_build_query轉成get形式的字串 'SAVE'=>'', //儲存路徑,不返回值 'PROXY'=>'', //proxy 'https'=>false, //HTTPS 'SSLVERSION'=>6, //0:DEFAULT 1:TLSv1 2:SSLv2 3:SSLv3 4:TLSv1.0 5:TLSv1.1 6:TLSv1.2 'ERROR_CODE'=>'N', //N:不顯示錯誤訊息 Y:顯示錯誤訊息 'HTTPHEADER'=>'', //自訂herder 'GET_INFO'=>'N', //Y:不回傳response,改回傳curl_getinfo的array ); //處理proxy if( !isset($mySetting['PROXY']) ){ if( isset($is_test_server) && $is_test_server == false ){ //正式機 //$mySetting['PROXY'] = 'sproxy.coa.gov.tw:8080'; } } //處理url $url = trim($url); if( substr($url,0,8) == 'https://' ){ //$url = substr($url,8); $mySetting['https'] = true; }elseif( substr($url,0,7) == 'http://' ){ //$url = substr($url,7); } if( !isset($mySetting['https']) && stristr($url,'gov.tw') ) $mySetting['https'] = true; //載入客製設定 if( is_array($mySetting) ){ foreach( $mySetting as $field=>$value ){ if( isset($setting[$field]) ){ $setting[$field] = $value; } } } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); //模擬瀏覽器 curl_setopt($ch, CURLOPT_USERAGENT,$setting['USERAGENT']); curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); //重新導向 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_MAXREDIRS, $setting['MAXREDIRS']); //限制最多導向5次 //timeout時間 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $setting['CONNECTTIMEOUT']); curl_setopt($ch, CURLOPT_TIMEOUT, $setting['TIMEOUT']); //是否取得內容與標頭 curl_setopt($ch, CURLOPT_NOBODY, $setting['NOBODY']); curl_setopt($ch, CURLOPT_HEADER, $setting['HEADER']); //https協定 if( $setting['https'] == true ) { curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSLVERSION, $setting['SSLVERSION'] ); } //proxy if( $setting['PROXY'] ){ curl_setopt($ch, CURLOPT_PROXY, $setting['PROXY']); } //POST模式 if( isset($setting['POST_DATA']) && $setting['POST_DATA'] ){ if( is_array($setting['POST_DATA']) && count($setting['POST_DATA'])>0 ){ //因為有些時候必須用字串來傳遞,所以新增字串型的post( by windgod 20200518 ) if( $setting['POST_BY_STR'] == "Y" ){ $setting['POST_DATA'] = http_build_query($setting['POST_DATA']); } } curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $setting['POST_DATA']); } //自訂HEADER if( is_array($setting['HTTPHEADER']) && count($setting['HTTPHEADER']) >0 ){ curl_setopt($ch, CURLOPT_HTTPHEADER, $setting['HTTPHEADER'] ); } //結果存成檔案 if( $setting['SAVE'] && is_dir(dirname($setting['SAVE'])) ){ curl_setopt($ch, CURLOPT_FILE, fopen($setting['SAVE'],"w")); } //echo "$url($setting[https] , $setting[SSLVERSION])\n"; //執行cURL $response = curl_exec($ch); if($setting['ERROR_CODE']=='Y'){ if($response === false) { echo 'Curl error: ' . curl_error($ch); } } //改回傳info,如果info拿到json的話,就改成array ( add by windgod 20200518 ) if( $setting['GET_INFO'] == "Y" ){ $response = curl_getinfo($ch); $temp = json_decode($response,true); if( json_last_error() == JSON_ERROR_NONE ){ $response = $temp; } } curl_close($ch); return $response; } function ip_control(){ //監控來源IP $ALLOW_IP = array( '61.62.19.215', //會議室無限網路 '210.201.26.221', //windgod '210.69.75.17', //農委會 '219.91.112.251', //windgod '127.0.0.1', //本機 '60.250.173.190',//二樓 '114.32.11.178',//新一樓 ); $ALLOW_IP_MASK ='192.168.1'; //內網 $ALLOW_IP_MASK_COA ='172.16'; //農委會 $mask = explode('.',$_SERVER['REMOTE_ADDR']); $new_mask = $mask[0].'.'.$mask[1].'.'.$mask[2]; $new_mask_coa=$mask[0].'.'.$mask[1]; $is_allow='0'; foreach($ALLOW_IP as $k => $v){ if($v==$_SERVER['REMOTE_ADDR']){ $is_allow='1'; } } if($new_mask!=$ALLOW_IP_MASK && $new_mask_coa!=$ALLOW_IP_MASK_COA){ $is_allow_new='0'; } else{ $is_allow_new='1'; } if($is_allow=='0' && $is_allow_new=='0'){ return '0'; } else return '1'; } //是否為行動裝置 2019/09/24 by windgod function isMobileCheck(){ $pattern = '/ipod|iphone|ipad|android|googlebot|mobile|webos|BlackBerry|Tablet/i'; return preg_match($pattern,$_SERVER['HTTP_USER_AGENT']); } //取得用戶端IP 2020/07/07 by windgod function getUserIP() { $ipaddress = ''; $check_list = [ 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR' ]; foreach( $check_list as $v ){ if (isset($_SERVER[$v])){ $ipaddress = $_SERVER[$v]; break; } } //指令模式 if( $ipaddress == '' && isset($_SERVER['SSH_CLIENT']) ){ $ipaddress = explode(' ',$_SERVER['SSH_CLIENT'])[0]; } return $ipaddress; } //分出ip清單內屬於ipv4與ipv6的部分 20200903 by windgod function split_ip( $IP_list=[], $type='ipv4' ){ $ipv4_list = []; $ipv6_list = []; if( is_array($IP_list) && count($IP_list) > 0 ){ foreach( $IP_list as $IP ){ if( strpos($IP,':') ){ $ipv6_list[] = $IP; }else{ $ipv4_list[] = $IP; } } }else{ return []; } return ( $type == 'ipv4' )?$ipv4_list:$ipv6_list; } //檢查ipv6(參考岳志從網路抓來的程式) 20200903 by windgod class ipv6{ public function ExpandIPv6Notation2Bin($ip6) { if (strpos($ip6, '::') !== false) $ip6 = str_replace('::', str_repeat(':0', 8 - substr_count($ip6, ':')).':', $ip6); $ip6parts = explode(':', $ip6); $res=""; foreach ($ip6parts as $part) $res .= str_pad(base_convert( $part, 16, 2), 16, 0, STR_PAD_LEFT); return $res; } public function MatchCIDR6( $cidr6, $chkipv6) { list($ip6, $prefixlen) = explode('/', $cidr6); $cidrbin= substr( $this->ExpandIPv6Notation2Bin($ip6), 0, $prefixlen); $chkip6bin= substr( $this->ExpandIPv6Notation2Bin($chkipv6), 0, $prefixlen); if(! strcmp($cidrbin,$chkip6bin))return true; return false; } } //檢查IP是否在IP清單中 20200903 by windgod /* IP可以用*和-代表區間,*等於0-255,*與-只能放在出現一次,例如192.168.1-10或172.*;ipv6不能用*和- */ function IP_match( $IP, $IP_list=[], $hash='' ){ //取得允許的清單 if( !$hash ){ $hash = md5(microtime(true)); if( is_string($IP_list) ){ $IP_list = explode(',',$IP_list); }elseif( !is_array($IP_list) ){ return false; } $IP_list_ipv4 = split_ip($IP_list,'ipv4'); $IP_list_ipv6 = split_ip($IP_list,'ipv6'); $GLOBALS['tmp_ip_list_ipv4_'.$hash] = $IP_list_ipv4; $GLOBALS['tmp_ip_list_ipv6_'.$hash] = $IP_list_ipv6; }else{ $IP_list_ipv4 = $GLOBALS['tmp_ip_list_ipv4_'.$hash]; $IP_list_ipv6 = $GLOBALS['tmp_ip_list_ipv6_'.$hash]; } //當有用,分隔格ip時,各別處理 if( strpos($IP,',') ){ $tmp = explode(',',$IP); foreach( $tmp as $sub_IP ){ if( IP_match($sub_IP,[],$hash) ){ return true; } } return false; } if( strpos($IP,':') ){ //ipv6 $ipv6_obj = new ipv6(); foreach($IP_list_ipv6 as $allow_ip){ $obj_ipv6 = new ipv6(); if( $obj_ipv6->MatchCIDR6($allow_ip,$IP)){ return true; } } }else{ //ipv4 foreach($IP_list_ipv4 as $allow_ip){ //先把*轉成0-255 if( strpos($allow_ip,'*') ){ $allow_ip = str_replace('*','0-255',$allow_ip); } //如果有-就要計算是否符合區間限制 if( strpos($allow_ip,'-') ){ $tmp = explode('.',$allow_ip); $last_range = array_pop($tmp); $last_block = explode('.',$IP)[ count($tmp) ]; $front_IP = implode('.',$tmp).'.'; if( substr($IP,0,strlen($front_IP)) == $front_IP ){ if( $last_range == '0-255' ){ return true; }else{ $tmp = explode('-',$last_range); if( $last_block >= $tmp[0] && $last_block <= $tmp[1] ){ return true; } } } }else{ if( $allow_ip == $IP ) return true; } } } return false; } //存在且有值 function ok( $val ){ return (isset($val)&&$val); } //檢測DOS攻擊 function check_DOS( $white_list=[], $goToUrl='' ){ //IP $clientIP = getUserIP(); //白名單直接通過 if( is_string($white_list) ) $white_list = explode(',',$white_list); if( count($white_list)>0 && IP_match( $clientIP, $white_list ) ){ return true; } $redis = new Redis(); $redis->connect('127.0.0.1', 6379); //閥值設定( 秒=>次數 ) $threshold = [ '30' => '30', '60' => '80', '3600' => '2000', ]; //封鎖時間 $block_sec = 600; //10分鐘 //時間 $currentTime = microtime(true); //redis key $counterKey = 'dos_counter:' . $clientIP; //刪除超過最大秒數的沒用紀錄 $max_sec = 0; foreach( $threshold as $sec=>$count ){ if( $sec > $max_sec ) $max_sec = $sec; } $outTime = $currentTime - $max_sec; $redis->zRemRangeByScore($counterKey, '-inf', $outTime); //新增一筆連線紀錄 $redis->zAdd($counterKey, $currentTime, $currentTime); $redis->expire($counterKey, $max_sec); //將最大閥值秒數設定為過期時間 //print_r($redis->zRange($counterKey,0,-1)); //檢查是不是被封鎖中 $notify_key = 'dos_notify:'.$clientIP; if( $redis->get($notify_key) ){ if( isset($_REQUEST['show_dos']) && $_REQUEST['show_dos'] == 'Y' ){ $unblockTime = date("Y-m-d H:i:s",$redis->get($notify_key)+$block_sec); echo "DOS封鎖至 $unblockTime\n";exit; } if( $goToUrl ){ header("Location:$goToUrl"); exit; }else{ header("HTTP/1.1 404 Not Found"); exit; } } $sn = 1; foreach( $threshold as $sec=>$count ){ $secAgo = $currentTime - (int)$sec; $connectionCount = $redis->zCount($counterKey, $secAgo, '+inf'); if( isset($_REQUEST['show_dos']) && $_REQUEST['show_dos'] == 'Y' ){ echo "閥值$sn : ${sec}秒內不可超過${count}次 => $connectionCount\n\n"; } if( $connectionCount > $count ){ //LINE通知 if( !$redis->get($notify_key) ){ $serverIP = $_SERVER['SERVER_ADDR']; $filePath = str_replace('/home/web/','',$_SERVER['SCRIPT_FILENAME']); $message = urlencode("${sec}秒內達到${connectionCount}次"); file("https://broadcast.imita.org/5/$serverIP,$filePath,$clientIP,$message"); $redis->set($notify_key,time()); $redis->expire($notify_key, $block_sec); //設定過期時間 if( isset($_REQUEST['show_dos']) && $_REQUEST['show_dos'] == 'Y' ){ echo "發通知\n"; exit; } }else{ if( isset($_REQUEST['show_dos']) && $_REQUEST['show_dos'] == 'Y' ){ echo "等待下次發通知\n"; exit; } } if( $goToUrl ){ header("Location:$goToUrl"); exit; }else{ header("HTTP/1.1 404 Not Found"); exit; } } $sn++; } } function send_mail_async($url,$subject,$html,$mail,$func,$mail_name,$website_name,$file_json,$site_org){ // 呼叫 send_email.php,傳遞收件者 email //echo $url;exit; //async_http_request($url, ["subject" => $subject,"html" => $html,"send_ma" => $mail,"func" => $func,"mail_name" => $mail_name,"website_name" => $website_name]); $curl_setting['POST_DATA']['subject'] = $subject; $curl_setting['POST_DATA']['html'] = $html; $curl_setting['POST_DATA']['send_ma'] = $mail; $curl_setting['POST_DATA']['func'] = $func; $curl_setting['POST_DATA']['mail_name'] = $mail_name; $curl_setting['POST_DATA']['website_name'] = $website_name; $curl_setting['POST_DATA']['site_org'] = $site_org; $curl_setting['POST_DATA']['file_json'] = $file_json; $curl_setting['CURLOPT_RETURNTRANSFER']=false; WG_curl($url,$curl_setting); } //防DOS攻擊 $white_list = [ //白名單 '172.16-31', //會內與所屬 '60.250.173.190','60.250.173.165', //冠諭 '122.116.17.35','61.219.51.42-45','210.241.129.2', //監控 '211.79.205.22', //弱掃 '117.56.14.169', //彰化管理處 '210.61.124.97', //桃園管理處 '220.128.234.37', //桃園管理處 '211.22.232.31', //雲林管理處 '117.56.151.127', //以下石門管理處 '117.56.151.128', '117.56.151.129', '117.56.151.130', '117.56.151.131', '117.56.151.132', '210.61.125.157', '210.61.125.158', '210.61.125.159', '210.61.125.160', '210.61.125.161', '117.56.231.19', '117.56.231.20', '117.56.231.21', '117.56.231.22', '117.56.231.23', '117.56.231.24', //以上石門管理處 '163.29.51.124', //宜蘭管理處 '210.61.131.37', //南投管理處 '52.233.106.1-255', //微軟 '210.241.95.44', //田邊好幫手 '210.241.95.225', //農業資料開放平臺 '211.79.206.224', //eDC IP '60.250.64.134', //獸醫所獸醫科技資訊網廠商 '220.128.243.37', //農水署桃園管理處 '210.61.124.97', //農水署桃園管理處 '211.21.98.136', //安碁網頁弱掃IP '211.21.98.137', //安碁網頁弱掃IP '211.21.98.138', //安碁網頁弱掃IP '220.218.130.212', //正哲弱掃IP '125.227.27.199', //農科院 '125.227.27.200', //農科院 '125.227.27.201', //農科院 '125.227.27.202', //農科院 '125.227.27.203', //農科院 '125.227.27.204', //農科院 '50.50.50.30', //農科院 '220.128.130.212', //朝峰抓資料 '125.229.89.203', //弱掃 '211.23.209.129', //弱掃 '220.133.18.152', //弱掃 ]; check_DOS( $white_list );