XSS安全处理
Security.class.php文件
1 <?php 2 class Security { 3 4 public $filename_bad_chars = array( 5 '../', '<!--', '-->', '<', '>', 6 "'", '"', '&', '$', '#', 7 '{', '}', '[', ']', '=', 8 ';', '?', '%20', '%22', 9 '%3c', // < 10 '%253c', // < 11 '%3e', // > 12 '%0e', // > 13 '%28', // ( 14 '%29', // ) 15 '%2528', // ( 16 '%26', // & 17 '%24', // $ 18 '%3f', // ? 19 '%3b', // ; 20 '%3d' // = 21 ); 22 protected $_xss_hash = ''; 23 protected $_never_allowed_str = array( 24 'document.cookie' => '[removed]', 25 'document.write' => '[removed]', 26 '.parentNode' => '[removed]', 27 '.innerHTML' => '[removed]', 28 '-moz-binding' => '[removed]', 29 '<!--' => '<!--', 30 '-->' => '-->', 31 '<![CDATA[' => '<![CDATA[', 32 '<comment>' => '<comment>' 33 ); 34 protected $_never_allowed_regex = array( 35 'javascript\s*:', 36 '(document|(document\.)?window)\.(location|on\w*)', 37 'expression\s*(\(|&\#40;)', // CSS and IE 38 'vbscript\s*:', // IE, surprise! 39 'Redirect\s+302', 40 "([\"'])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?" 41 ); 42 43 public function gprc_xss(){ 44 if($_POST)$_POST = $this->xss_clean($_POST); 45 if($_GET)$_GET = $this->xss_clean($_GET); 46 if($_COOKIE)$_COOKIE = $this->xss_clean($_COOKIE); 47 if($_REQUEST)$_REQUEST = $this->xss_clean($_REQUEST); 48 } 49 50 public function remove_invisible_characters($str, $url_encoded = TRUE){ 51 $non_displayables = array(); 52 if ($url_encoded) { 53 $non_displayables[] = '/%0[0-8bcef]/'; // url encoded 00-08, 11, 12, 14, 15 54 $non_displayables[] = '/%1[0-9a-f]/'; // url encoded 16-31 55 } 56 $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127 57 do { 58 $str = preg_replace($non_displayables, '', $str, -1, $count); 59 } 60 while ($count); 61 return $str; 62 } 63 64 public function xss_clean($str, $is_image = FALSE){ 65 if (is_array($str)){ 66 while (list($key) = each($str)){ 67 $str[$key] = $this->xss_clean($str[$key]); 68 } 69 return $str; 70 } 71 $str = $this->_validate_entities($this->remove_invisible_characters($str)); 72 $str = rawurldecode($str); 73 $str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str); 74 $str = preg_replace_callback('/<\w+.*/si', array($this, '_decode_entity'), $str); 75 $str = $this->remove_invisible_characters($str); 76 $str = str_replace("\t", ' ', $str); 77 $converted_string = $str; 78 $str = $this->_do_never_allowed($str); 79 if ($is_image === TRUE){ 80 $str = preg_replace('/<\?(php)/i', '<?\\1', $str); 81 }else { 82 $str = str_replace(array('<?', '?'.'>'), array('<?', '?>'), $str); 83 } 84 $words = array( 85 'javascript', 'expression', 'vbscript', 'script', 'base64', 86 'applet', 'alert', 'document', 'write', 'cookie', 'window' 87 ); 88 foreach ($words as $word){ 89 $word = implode('\s*', str_split($word)).'\s*'; 90 $str = preg_replace_callback('#('.substr($word, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str); 91 } 92 do{ 93 $original = $str; 94 if (preg_match('/<a/i', $str))$str = preg_replace_callback('#<a\s+([^>]*?)(?:>|$)#si', array($this, '_js_link_removal'), $str); 95 if (preg_match('/<img/i', $str))$str = preg_replace_callback('#<img\s+([^>]*?)(?:\s?/?>|$)#si', array($this, '_js_img_removal'), $str); 96 if (preg_match('/script|xss/i', $str))$str = preg_replace('#</*(?:script|xss).*?>#si', '[removed]', $str); 97 } 98 while ($original !== $str); 99 unset($original); 100 $str = $this->_remove_evil_attributes($str, $is_image); 101 $naughty = 'alert|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|isindex|layer|link|meta|object|plaintext|style|script|textarea|title|video|xml|xss'; 102 $str = preg_replace_callback('#<(/*\s*)('.$naughty.')([^><]*)([><]*)#is', array($this, '_sanitize_naughty_html'), $str); 103 $str = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', '\\1\\2(\\3)', $str); 104 $str = $this->_do_never_allowed($str); 105 if ($is_image === TRUE)return ($str === $converted_string); 106 return $str; 107 } 108 109 public function xss_hash(){ 110 if ($this->_xss_hash === '')$this->_xss_hash = md5(uniqid(mt_rand())); 111 return $this->_xss_hash; 112 } 113 114 public function entity_decode($str, $charset = NULL){ 115 if (strpos($str, '&') === FALSE)return $str; 116 if (empty($charset))$charset = 'utf-8'; 117 118 do{ 119 $matches = $matches1 = 0; 120 $str = preg_replace('~(�*[0-9a-f]{2,5});?~iS', '$1;', $str, -1, $matches); 121 $str = preg_replace('~(&#\d{2,4});?~S', '$1;', $str, -1, $matches1); 122 $str = html_entity_decode($str, ENT_COMPAT, $charset); 123 } 124 while ($matches OR $matches1); 125 return $str; 126 } 127 128 public function sanitize_filename($str, $relative_path = FALSE){ 129 $bad = $this->filename_bad_chars; 130 if ( ! $relative_path){ 131 $bad[] = './'; 132 $bad[] = '/'; 133 } 134 $str = $this->remove_invisible_characters($str, FALSE); 135 do{ 136 $old = $str; 137 $str = str_replace($bad, '', $str); 138 }while ($old !== $str); 139 return stripslashes($str); 140 } 141 142 public function strip_image_tags($str){ 143 return preg_replace(array('#<img[\s/]+.*?src\s*=\s*["\'](.+?)["\'].*?\>#', '#<img[\s/]+.*?src\s*=\s*(.+?).*?\>#'), '\\1', $str); 144 } 145 146 protected function _compact_exploded_words($matches){ 147 return preg_replace('/\s+/s', '', $matches[1]).$matches[2]; 148 } 149 150 protected function _remove_evil_attributes($str, $is_image){ 151 $evil_attributes = array('style', 'xmlns', 'formaction'); 152 if ($is_image === TRUE)unset($evil_attributes[array_search('xmlns', $evil_attributes)]); 153 154 do { 155 $count = 0; 156 $attribs = array(); 157 preg_match_all('/('.implode('|', $evil_attributes).')\s*=\s*(\042|\047)([^\\2]*?)(\\2)/is', $str, $matches, PREG_SET_ORDER); 158 foreach ($matches as $attr){ 159 $attribs[] = preg_quote($attr[0], '/'); 160 } 161 preg_match_all('/('.implode('|', $evil_attributes).')\s*=\s*([^\s>]*)/is', $str, $matches, PREG_SET_ORDER); 162 foreach ($matches as $attr){ 163 $attribs[] = preg_quote($attr[0], '/'); 164 } 165 if (count($attribs) > 0)$str = preg_replace('/(<?)(\/?[^><]+?)([^A-Za-z<>\-])(.*?)('.implode('|', $attribs).')(.*?)([\s><]?)([><]*)/i', '$1$2 $4$6$7$8', $str, -1, $count); 166 } 167 while ($count); 168 return $str; 169 } 170 171 protected function _sanitize_naughty_html($matches) { 172 return '<'.$matches[1].$matches[2].$matches[3].str_replace(array('>', '<'), array('>', '<'), $matches[4]); 173 } 174 175 protected function _js_link_removal($match){ 176 return str_replace($match[1], 177 preg_replace('#href=.*?(?:alert\(|alert&\#40;|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si', 178 '', $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])) ),$match[0]); 179 } 180 181 protected function _js_img_removal($match){ 182 return str_replace($match[1], 183 preg_replace('#src=.*?(?:alert\(|alert&\#40;|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si', 184 '',$this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])) 185 ), 186 $match[0]); 187 } 188 189 protected function _convert_attribute($match){ 190 return str_replace(array('>', '<', '\\'), array('>', '<', '\\\\'), $match[0]); 191 } 192 193 protected function _filter_attributes($str){ 194 $out = ''; 195 if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches)){ 196 foreach ($matches[0] as $match){ 197 $out .= preg_replace('#/\*.*?\*/#s', '', $match); 198 } 199 } 200 return $out; 201 } 202 203 protected function _decode_entity($match){ 204 return $this->entity_decode($match[0], strtoupper('utf-8')); 205 } 206 207 protected function _validate_entities($str){ 208 $str = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-]+)|i', $this->xss_hash().'\\1=\\2', $str); 209 $str = preg_replace('#(&\#?[0-9a-z]{2,})([\x00-\x20])*;?#i', '\\1;\\2', $str); 210 $str = preg_replace('#(&\#x?)([0-9A-F]+);?#i', '\\1\\2;', $str); 211 return str_replace($this->xss_hash(), '&', $str); 212 } 213 214 protected function _do_never_allowed($str){ 215 $str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str); 216 foreach ($this->_never_allowed_regex as $regex){ 217 $str = preg_replace('#'.$regex.'#is', '[removed]', $str); 218 } 219 return $str; 220 } 221 /** 222 * @desc: 暂时不用 223 * */ 224 public function gprc_sql(){ 225 if(!get_magic_quotes_gpc()) { 226 if($_POST) $_POST = $this->daddslashes($_POST); 227 if($_GET) $_GET = $this->daddslashes($_GET); 228 if($_COOKIE) $_COOKIE = $this->daddslashes($_COOKIE); 229 if($_REQUEST) $_REQUEST = $this->daddslashes($_REQUEST); 230 } 231 } 232 233 public function daddslashes($string){ 234 if(!is_array($string)) return addslashes($string); 235 foreach($string as $key => $val) $string[$key] = $this->daddslashes($val); 236 return $string; 237 } 238 }
调用方式:
$SEC = new Security();
$SEC->gprc_xss();
$SEC = NULL;