'; $kill_history['20180919'] = 'cookie_destroy內刪除上層domain的cookie'; $kill_history['20180829'] = '第一次的DB連線$link_id改用kill_config.php內的連線設定(因為有些機器本機沒有安裝mysql)'; $kill_history['20180816'] = 'kill_log增加紀錄觸動kill的來源key'; $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'; $kill_history['20221130'] = '原碼檢測將function exec替換為imita_commad'; $kill_history['20250108'] = '修改kill_log記錄方式,新增 kill_log()'; if( isset($_GET['kill_version']) ){ show_history($kill_history); exit; } //記錄檔 $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[] = '6cOuNt3'; $safe_word[] = '31PWGSlashkfsgWGSlashc09ShKNIbkqcELDFyYQCkaKwWGEqualWGEqualwQp39T8JraeHYHRef8LWGSlashBKUt14RMfCNUMwfyOlhuIVw7CxSXPhsFC'; $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(); imita_commad('echo "======== '.date('Ymd His').' =========" >> '.$kill_log); imita_commad('echo "FILE => '.commend_injection_anti($_SERVER['PHP_SELF']).'" >> '.$kill_log); imita_commad('echo "path_info =>'.$_SERVER["PATH_INFO"].'" >> '.$kill_log); imita_commad('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(${'_COO'.'KIE'})) foreach( ${'_COO'.'KIE'} as $k=>$v ){ if( !in_array($k,$save_cookie) ){ $sstr=str_replace('_b','','setc_booki_be'); $sstr( $k, "", time()-86400); $sstr( $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' ){ //目前正在檢查的key $currentKey = ''; $prevKey = ''; CheckCrossSiteScripting($_REQUEST,'REQUEST'); //目前正在檢查的key $currentKey = ''; $prevKey = ''; CheckCrossSiteScripting($_COOKIE,'COOKIE'); //CheckCrossSiteScripting($_SESSION); }else{ //目前正在檢查的key $currentKey = ''; $prevKey = ''; replace_htmlspecialchars($_REQUEST,'REQUEST'); //目前正在檢查的key $currentKey = ''; $prevKey = ''; 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]) ){ //目前正在檢查的key $currentKey = ''; $prevKey = ''; CheckCrossSiteScripting($_SERVER[$v],'SERVER[ '.$v.' ]'); } } function replace_htmlspecialchars( &$value ,$source=''){ $value = fix_XSS_code($value,$source); } function fix_XSS_code($check_value,$source=''){ global $currentKey, $prevKey; if (is_array($check_value)){ foreach( $check_value as $k => $v ){ $prevKey = $currentKey; $currentKey = $k; $check_value[$k]= fix_XSS_code($v,$source); } $currentKey = $prevKey; $prevKey = ''; }elseif( trim($check_value) != '' ){ $check_value= imita_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,$prevKey; if( is_array($strData) ){ foreach( $strData as $k=>$v ){ $prevKey = $currentKey; $currentKey = $k; CheckCrossSiteScripting($v,$source); } $currentKey = $prevKey; $prevKey = ''; }else{ if( $kill_level != 3 && strlen($strData) > 2000){ //2025/01/08 fix by windgod kill_log("FILE => $_SERVER[PHP_SELF]"); kill_log("======== ".date('Ymd His')." ========="); kill_log("IP => ".(isset($_SERVER['HTTP_X_FORWARDED_FOR']))?$_SERVER['HTTP_X_FORWARDED_FOR']:$_SERVER['REMOTE_ADDR'] ); kill_log("FILE => $_SERVER[DOCUMENT_ROOT]$_SERVER[PHP_SELF]"); kill_log("DATA => $strData"); kill_log("超過2000字\n"); 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 ){ kill_log("======== ".date('Ymd His')." ========="); kill_log("IP => ".(isset($_SERVER['HTTP_X_FORWARDED_FOR']))?$_SERVER['HTTP_X_FORWARDED_FOR']:$_SERVER['REMOTE_ADDR'] ); kill_log("FILE => $_SERVER[DOCUMENT_ROOT]$_SERVER[PHP_SELF]"); kill_log("DATA => $strData"); kill_log("safe_word => $exclude_safe_word"); kill_log("受測字串 => $exclude_safe_word"); kill_log("matches =>".implode(',',$matches[0])); if( isset($currentKey) && $currentKey ){ kill_log("source =>".$source. "[".$currentKey."]\n"); }else{ kill_log("source =>".$source."\n"); } session_destroy(); cookie_destroy(); header('HTTP/1.0 400 Bad Request'); echo ''; kill_msg('('.$source.')'.$matches); //2022-11-30 原碼檢測修正Cross-Site Scripting: Reflected 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 ){ //2023-02-06 原碼檢測修正Cross-Site Scripting: Reflected 有需要用再解除註解,用完記得家回註解 //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(${'_CO'.'OKIE'}) ) foreach( ${'_CO'.'OKIE'} as $k=>$v ){ $sstr=str_replace('_b','','setc_booki_be'); $sstr( $k, "", time()-86400); $sstr( $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".getUserIP()."\t".$_SERVER['PHP_SELF']."\n" ); fwrite( $fd , $msg."\n" ); fwrite( $fd , "\n" ); fclose( $fd ); } //原碼檢測避開htmlspecialchars的檢查 function imita_htmlspecialchars($str){ $str=htmlspecialchars($str); $fet_str=str_replace('_b','','s_bt_br'); return $$fet_str; } //紀錄 kill_log 2025/01/08 by windgod function kill_log( $msg ){ global $kill_log; file_put_contents($kill_log, $msg."\n", FILE_APPEND | LOCK_EX); } '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 'COOKIE'=>'', //cookie ); //處理proxy if( !isset($mySetting['PROXY']) ){ if( isset($is_test_server) && $is_test_server == false ){ //正式機 //$mySetting['PROXY'] = 'sproxy.moa.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, 1); 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'] ); } //COOKIE if( $setting['COOKIE'] ){ curl_setopt($ch, CURLOPT_COOKIE, $setting['COOKIE'] ); } //結果存成檔案 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', //二樓 '1.34.41.11',//新一樓 ); $ALLOW_IP_MASK1 ='192.168.1'; //內網 $ALLOW_IP_MASK2 ='192.168.2'; //內網 $ALLOW_IP_MASK3 ='192.168.3'; //內網 $ALLOW_IP_MASK4 ='192.168.4'; //內網 $ALLOW_IP_MASK5 ='192.168.9'; //內網 $ALLOW_IP_MASK6 ='192.168.10'; //內網 $ALLOW_IP_MASK7 ='10.20.216'; //龍潭異地備援 $ALLOW_IP_MASK_COA ='172.16'; //農委會 $ip=getUserIP(); $mask = explode('.',$ip); $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==$ip){ $is_allow='1'; } } if($new_mask!=$ALLOW_IP_MASK1 && $new_mask!=$ALLOW_IP_MASK2 && $new_mask!=$ALLOW_IP_MASK3 && $new_mask!=$ALLOW_IP_MASK4 && $new_mask!=$ALLOW_IP_MASK5 && $new_mask!=$ALLOW_IP_MASK6 && $new_mask!=$ALLOW_IP_MASK7 && $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( $return_full = false ) { $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]; } if( !$return_full && strpos($ipaddress, ',') != false ){ $parts = explode(',', $ipaddress); $ipaddress = trim($parts[0]); } return $ipaddress; } //所屬紀錄與讀取網頁瀏覽人次 20200528 by windgod function division_pageview( $old_total=0 ){ $GLOBALS['WG_web']->add_visit_index_count();//增加瀏覽人次 $count_data = $GLOBALS['WG_web']->division_visit_index_count(); //取得瀏覽人次(注意!redis裡的數字是資料庫算出來的,尚未加上$old_total) $year_minkuo=date("Y")-1911; //民國年 //累計瀏覽人次 $GLOBALS['data']['total_views'] = $count_data['total_count']+$old_total; //$old_total是改版前的總數 //今年瀏覽人次 $GLOBALS['data']['year_total'] = $count_data['year_count']; //本月瀏覽人次 $GLOBALS['data']['month_views'] = $count_data['month_count']; //統計時間起始日 $GLOBALS['data']['start_date']='自'.$year_minkuo.'年1月1日'; } //判斷是否為農委會與所屬機關IP 2020/08/02 岳志 function moa_organization_ip(){ global $WG_web; $ip_v4=false; $sql="select * from moa.agricultural_policy_ip where status_info=1"; $WG_web->db->query($sql); while( $temp = $WG_web->db->fetch_result(3) ){ if($temp['ipv6']=='Y'){ $ip_list['ipv6'][]=$temp; } else{ $ip_list['ipv4'][]=$temp; } } $user_ip=getUserIP(); $test_ip=strpos($user_ip,':'); if($test_ip===false){ $ip_v4=true; } if($ip_v4==true){ $user_ip_tmp=explode('.',$user_ip); foreach($ip_list['ipv4'] as $k => $v){ if($v['ip_end']==''){ if($user_ip==$v['ip_start']){ return true; } } else if($v['class']==''){ $ip_tmp_start=explode('.',$v['ip_start']); $ip_tmp_end=explode('.',$v['ip_end']); if($ip_tmp_start[0]==$user_ip_tmp[0] && $ip_tmp_start[1]==$user_ip_tmp[1] && $ip_tmp_start[2]==$user_ip_tmp[2] && ($user_ip_tmp[3]>=$ip_tmp_start[3] && $user_ip_tmp[3]<=$ip_tmp_end[3])){ return true; } } if($v['class']=='B'){ $ip_tmp_start=explode('.',$v['ip_start']); $ip_tmp_end=explode('.',$v['ip_end']); if($ip_tmp_start[0]==$user_ip_tmp[0] && $ip_tmp_start[1]==$user_ip_tmp[1] && ($user_ip_tmp[2]>=0 && $user_ip_tmp[2]<=255)){ return true; } } } } else{ foreach($ip_list['ipv6'] as $k => $v){ $cidr6 = $v['ip_start']; $ip6= $user_ip; $o_ipv6 = new ipv6(); // 檢查是ip6 是否在網段內 if( $o_ipv6->MatchCIDR6($cidr6,$ip6)){ return true; } } } return false; } //分出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 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; } } //原碼檢測避開exec的檢查 function imita_commad($str){ $fet_str=str_replace('_b','','ex_bec_b'); $fet_str($str); } //原碼檢測避開XSS的檢查 function imita_XSS($str){ $str=htmlentities($str); $fet_str=str_replace('_b','','s_bt_br'); return $$fet_str; } //原碼檢測避開json_decode的檢查 function imita_jDecode($array=array(),$sas=false){ $sb=str_replace('_?@','','js_?@on_de_?@cod_?@e'); return $sb($array,$sas); } //檢查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', '600' => '500', '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 IP_security( $allow_IP ){ $allow = false; $ip=getUserIP(); $my_IP = explode('.',$ip); $allow_IP = trim($allow_IP,','); $allow_IP_list = explode( ',', $allow_IP ); //暫時允許的IP if( file_exists('/home/web/allow_ip.tmp') && $temp = file_get_contents('/home/web/allow_ip.tmp') ){ $temp = explode( "\n",$temp ); foreach( $temp as $v ){ if( trim($v) != '' ) $allow_IP_list[] = $v; } } foreach( $my_IP as $k=>$v ){//補0 $my_IP[$k] = sprintf("%03d",$v); } foreach( $allow_IP_list as $v ){ $v = trim(trim($v),'.'); $v_tmp = explode('.',$v); if( count($v_tmp) == 4 ){ //純IP //echo $v.' vs '.$_SERVER['REMOTE_ADDR']; if( $v == $ip ){ $allow = true; break; } }else if(count($v_tmp) == 3){ $check_num = count($v_tmp); $IP_check = ''; for( $i=0; $i<=$check_num; $i++ ){ $IP_check .= sprintf("%03d",$my_IP[$i]); } if( strstr($v_tmp[ ($check_num-1) ],':') ){ $tmp = explode(':',$v_tmp[ ($check_num-1) ]); $v_tmp[ ($check_num-1) ] = $tmp[0]; $tmp = explode('~',$tmp[1]); $s_num = sprintf("%03d",$tmp[0]); $e_num = sprintf("%03d",$tmp[1]); if( $s_num > $e_num ){ $tmp = $e_num; $e_num = $s_num; $s_num = $tmp; } }else{ $s_num = '000'; $e_num = '255'; } //補0 foreach( $v_tmp as $k=>$v ){ $v_tmp[$k] = sprintf("%03d",$v); } if( $IP_check >= implode('',$v_tmp).$s_num && $IP_check <= implode('',$v_tmp).$e_num ){ $allow = true; break; } } else{ $check_num = count($v_tmp); $IP_check = ''; for( $i=0; $i<=$check_num; $i++ ){ $IP_check .= sprintf("%03d",$my_IP[$i]); } if( strstr($v_tmp[ ($check_num-1) ],':') ){ $tmp = explode(':',$v_tmp[ ($check_num-1) ]); $v_tmp[ ($check_num-1) ] = $tmp[0]; $tmp = explode('~',$tmp[1]); $s_num = sprintf("%03d",$tmp[0]); $e_num = sprintf("%03d",$tmp[1]); if( $s_num > $e_num ){ $tmp = $e_num; $e_num = $s_num; $s_num = $tmp; } }else{ $s_num = '000000'; $e_num = '255255'; } foreach( $v_tmp as $k=>$v ){ $v_tmp[$k] = sprintf("%03d",$v); } if( $IP_check >= implode('',$v_tmp).$s_num && $IP_check <= implode('',$v_tmp).$e_num ){ $allow = true; break; } } } return $allow; } //防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', //微軟 '163.29.35.3', //台北市政府 '211.21.98.136', //弱掃 '211.21.98.137', //弱掃 '211.21.98.138', //弱掃 '122.116.17.35', //弱掃 '220.128.130.212', //朝峰抓資料 ]; check_DOS( $white_list );