_runInit = false; } /** * 禁止使用 clone 方式產生出另一個實體 * * @return void */ private function __clone() { } /** * 禁止修改未定義的變數 * * @return void */ public function __set($name, $value) { try { throw new Exception("禁止設定物件未定義變數"); } catch (Exception $e) { echo nl2br($e->__toString()) . "
"; } } /** * 禁止存取未定義的變數 * * @return void */ public function __get($name) { try { throw new Exception("沒有此變數,或者無法存取"); } catch (Exception $e) { echo nl2br($e->__toString()) . "
"; } } /** * 單一實體 * * @return self */ public static function getInstance() { if (null === self::$_instance) { self::$_instance = new self(); } return self::$_instance; } /** * 初始化 * * @return void */ public function init() { try { if (!$this->_runInit) { $this->initRequest()->register_globals(); $this->_runInit = true; } } catch (Exception $e) { echo nl2br($e->__toString()) . "
"; } return $this; } /** * 將陣列使用其元素名稱轉化成變數型態 * * @param array $array 要進行轉換的陣列 * @param string $type 轉換過程發生重複定義的處理方式 * @param string $prefix 發生重複定義時要加上的前綴名稱 * @return void */ public function _extract(&$array, $type = EXTR_OVERWRITE, $prefix = '') { if (!is_array($array)) { throw new Exception(__METHOD__ . '(): 第一個參數必須是陣列'); } if (!empty($prefix) && !preg_match('#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#', $prefix)) { throw new Exception(__METHOD__ . '(): 第三個參數必須是英文或者是底線'); } if (($type == self::EXTR_PREFIX_SAME || $type == self::EXTR_PREFIX_ALL || $type == self::EXTR_PREFIX_IF_EXISTS) && empty($prefix)) { throw new Exception(__METHOD__ . '(): 第三個參數是必須的'); } $prefix = $prefix . '_'; foreach ($array as $key => $val) { if (in_array($key, array("_GET", "_POST", "_COOKIE", "_ENV", "_SESSION", "_REQUEST", "_SERVER"))) continue; if (!is_array($array[$key])) { switch ($type) { default: case self::EXTR_OVERWRITE: $GLOBALS[$key] = $val; break; case self::EXTR_SKIP: $GLOBALS[$key] = isset($GLOBALS[$key]) ? $GLOBALS[$key] : $val; break; case self::EXTR_PREFIX_SAME: if (isset($GLOBALS[$key])) { $GLOBALS[$prefix . $key] = $val; } else { $GLOBALS[$key] = $val; } break; case self::EXTR_PREFIX_ALL: $GLOBALS[$prefix . $key] = $val; break; case self::EXTR_PREFIX_INVALID: if (!preg_match('#^[a-zA-Z_\x7f-\xff]$#', $key{ 0})) { $GLOBALS[$prefix . $key] = $val; } else { $GLOBALS[$key] = $val; } break; case self::EXTR_IF_EXISTS: if (isset($GLOBALS[$key])) { $GLOBALS[$key] = $val; } break; case self::EXTR_PREFIX_IF_EXISTS: if (isset($GLOBALS[$key])) { $GLOBALS[$prefix . $key] = $val; } break; case self::EXTR_REFS: $GLOBALS[$key] = &$array[$key]; break; } } else { $this->{__FUNCTION__}($array[$key], $type, $prefix); } } } /** * 將陣列資料設定為環境變數 * * @param array $array 將陣列設定為環境變數 * @return void */ public function _set_env($array) { foreach ($array as $key => $value) { putenv($key . '=' . $value); // apache_setenv($key, $value); } } /** * 將PHP內建的SuperGlobals變數陣列轉化為變數型態{@link _extract()} * * @return self */ public function register_globals() { if (ini_get('register_globals')) { $variables_order = preg_split('//', ((strlen(ini_get('variables_order'))) ? ini_get('variables_order') : $this->default_register_order)); foreach ($variables_order as $variable_type) { switch ($variable_type) { case "G": $this->_extract($_GET, self::EXTR_OVERWRITE); break; case "P": $this->_extract($_POST, self::EXTR_OVERWRITE); break; case "C": $this->_extract($_COOKIE, self::EXTR_OVERWRITE); break; case "E": $this->_extract($_ENV, self::EXTR_OVERWRITE); break; case "S": if (isset($_SESSION)) $this->_extract($_SESSION, self::EXTR_OVERWRITE); break; } } $this->_extract($_REQUEST, self::EXTR_OVERWRITE); $this->_extract($_SERVER, self::EXTR_OVERWRITE); } return $this; } /** * 初始化 client 回傳的變數 * * @param string $request_order 要進行過濾的Super Globals陣列變數 * @return self */ public function initRequest($request_order = 'EGPCS') { $request_order = preg_split('//', ((strlen($request_order)) ? $request_order : $this->default_request_order)); foreach ($request_order as $variable_type) { switch ($variable_type) { case "G": $pre_get_List_Ary = $_GET; $_GET = $this->filter($_GET); //針對各變數比對是否合法 if (count($_GET)) { if ($this->getTables() && $this->getGetCheck()) { foreach ($_GET as $val) { //比對GET的值是否有不一致 if (!in_array($val, $pre_get_List_Ary)) { putenv("bad_iid=TRUE"); } } } else if ($_GET['index'] != 'error') { putenv("bad_iid=TRUE"); } } //針對FTP攻擊設限 $chkAry = array("(", ")", "=", ">", "<", "true", "false", "@", "ftp", ":", ";"); foreach ($_GET as $gval) { foreach ($chkAry as $cval) { if (preg_match("/" . $cval . "/i", $gval)) { putenv("bad_iid=TRUE"); } } } break; case "P": $pre_post_List_Ary = $_POST; $_POST = $this->filter($_POST); //針對各變數比對是否合法 if (count($_POST)) { foreach ($_POST as $val) { //比對POST的值是否有不一致 if (!in_array($val, $pre_post_List_Ary)) { putenv("bad_iid=TRUE"); } } } break; case "C": $_COOKIE = $this->filter($_COOKIE); break; case "E": $_ENV = $this->filter($_ENV); break; case "S": if (isset($_SESSION)) $_SESSION = $this->filter($_SESSION); break; } } $_REQUEST = $this->filter($_REQUEST); $_SERVER = $this->filter($_SERVER); $tmp = array(); if (count($_GET)) { foreach ($_GET as $key => $value) { $tmp[] = $key . '=' . $value; } } $_SERVER["QUERY_STRING"] = implode("&", $tmp); if (strpos($_SERVER["REQUEST_URI"], "?")) { $_SERVER["REQUEST_URI"] = substr($_SERVER["REQUEST_URI"], 0, strpos($_SERVER["REQUEST_URI"], "?")) . "?" . $_SERVER["QUERY_STRING"]; } else { $_SERVER["REQUEST_URI"] = $_SERVER["REQUEST_URI"]; } $this->_set_env($_SERVER); return $this; } /** * 簡易型過濾字串中的非法字元 * * @param mixed $val 要進行安全性過濾的字串 * @return mixed */ public static function quote($val) { if ($source_charset = mb_detect_encoding($val)) { if ($source_charset != 'UTF-8') { $val = mb_convert_encoding(addslashes(mb_convert_encoding($val, "UTF-8", $source_charset)), $source_charset, "UTF-8"); } } return $val; } /** * 限定只能輸入英數字 * *@param *@return true/false */ public static function getGetCheck() { foreach ($_GET as $key => $val) { if ($_GET['index'] != 'fulltext' && $key != 'keyword') { if ($key == "iid") { if (preg_match("/^[0-9]+$/", $val) || $val == '') { } else { return false; } } else { if (preg_match("/^[_a-zA-Z0-9]+$/", $val) || $val == '') { } else { return false; } } } } return true; } /** * 限定數字 * *@param *@return true/false */ public static function getTables() { $funAidAry = array('lookinto', 'down', 'sitemap', 'feed', 'home', 'dhome', 'information', 'accessibility', 'privacy', 'webscty', 'inforscty', 'search', 'error','q2'); if (preg_match("/^[0-9]+$/", $_GET['aid']) || in_array($_GET['aid'], $funAidAry)) { } else { return false; } return true; } /** * 過濾字串中的非法字元 * * @param mixed $val 要進行安全性過濾的字串或陣列 * @return mixed */ public static function filter($val) { switch (strtolower(gettype($val))) { case 'array': foreach ($val as $key => $value) { unset($val[$key]); $safe_key = self::getInstance()->{__FUNCTION__}($key); $val[$safe_key] = self::getInstance()->{__FUNCTION__}($value); } return $val; break; case 'object': case 'resource': case 'boolean': case 'null': return $val; break; case 'integer': return (int) $val; break; case 'double': return (float) $val; break; case 'string': default: } $val = self::getInstance()->quote($val); $val = html_entity_decode($val); $val = htmlspecialchars_decode($val); $val = strip_tags($val); // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed // this prevents some character re-spacing such as // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs $val = preg_replace('/([\x00-\x08][\x0b-\x0c][\x0e-\x20])/', '', $val); // straight replacements, the user should never need these since they're normal characters // this prevents like $search = 'abcdefghijklmnopqrstuvwxyz'; $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; $search .= '1234567890!@#$%^&*()'; $search .= '~`";:?+/={}[]-_|\'\\'; for ($i = 0; $i < strlen($search); $i++) { // ;? matches the ;, which is optional // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars // @ @ search for the hex values $val = preg_replace('/(&#[x|X]0{0,8}' . dechex(ord($search[$i])) . ';?)/i', $search[$i], $val); // with a ; // @ @ 0{0,7} matches '0' zero to seven times $val = preg_replace('/(�{0,8}' . ord($search[$i]) . ';?)/', $search[$i], $val); // with a ; } // now the only remaining whitespace attacks are \t, \n, and \r $ra1 = array('ftp', 'javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'base'); $ra2 = array('window', 'onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload'); $ra = array_merge($ra1, $ra2); $found = true; // keep replacing as long as the previous round replaced something while ($found == true) { $val_before = $val; for ($i = 0; $i < sizeof($ra); $i++) { $pattern = '/'; for ($j = 0; $j < strlen($ra[$i]); $j++) { if ($j > 0) { $pattern .= '('; $pattern .= '(&#[x|X]0{0,8}([9][a][b]);?)?'; $pattern .= '|(�{0,8}([9][10][13]);?)?'; $pattern .= ')?'; } $pattern .= $ra[$i][$j]; } $pattern .= '/i'; $replacement = substr($ra[$i], 0, 2) . '' . substr($ra[$i], 2); // add in <> to nerf the tag $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags if ($val_before == $val) { // no replacements were made, so exit the loop $found = false; } } } return $val; } }