New version submitted by TomB
[carbonphp.git] / Source / carbon / libraries / Input.php
blob258cd441cd927924bbbd1794e3d2ed8529315667
1 <?php
2 /*------------------------------------------------------------
3 * CarbonPHP framework (C) Tom Bell
4 * http://tombell.org.uk
5 *------------------------------------------------------------*/
7 if (!defined('CARBON_PATH'))
9 exit('Direct script access is not allowed.');
12 class Carbon_Input
14 protected $use_xss_clean = false;
15 protected $ip_address = false;
16 protected $user_agent = false;
17 protected $allow_get_array = false;
19 public function __construct()
21 $config =& load_class('Config');
22 $this->use_xss_clean = ($config->get_config_item('use_xss_filtering') === true) ? true : false;
23 $this->allow_get_array = ($config->get_config_item('use_query_strings') === true) ? true : false;
24 $this->_sanitise_globals();
26 log_message('debug', 'Input.php - Carbon_Input class initialised');
29 protected function _sanitise_globals()
31 $protected = array(
32 '_SERVER',
33 '_GET',
34 '_POST',
35 '_FILES',
36 '_REQUEST',
37 '_SESSION',
38 '_ENV',
39 'GLOBALS',
40 'HTTP_RAW_POST_DATA',
41 'carbon_folder',
42 'application_folder',
43 'benchmark',
44 'ext',
45 'config',
46 'uri',
47 'router',
48 'output',
49 'input');
51 foreach (array($_GET, $_POST, $_COOKIE, $_SERVER, $_FILES, $_ENV, (isset($_SESSION) && is_array($_SESSION)) ? $_SESSION : array()) as $global)
53 if (!is_array($global))
55 if (!in_array($global, $protected))
57 unset($GLOBALS[$global]);
60 else
62 foreach ($global as $key => $val)
64 if (!in_array($key, $protected))
66 unset($GLOBALS[$key]);
69 if (is_array($val))
71 foreach ($val as $k => $v)
73 if (!in_array($k, $protected))
75 unset($GLOBALS[$k]);
83 if ($this->allow_get_array == false)
85 $_GET = array();
87 else
89 if (is_array($_GET) && count($_GET) > 0)
91 foreach ($_GET as $key => $val)
93 $_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
98 if (is_array($_POST) && count($_POST) > 0)
100 foreach ($_POST as $key => $val)
102 $_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
106 if (is_array($_COOKIE) && count($_COOKIE) > 0)
108 foreach ($_COOKIE as $key => $val)
110 $_COOKIE[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
114 log_message('debug', 'Input.php - Global GET, POST, and COOKIE arrays have been sanitised');
117 protected function _clean_input_data($string)
119 if (is_array($string))
121 $new_array = array();
123 foreach ($string as $key => $val)
125 $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
128 return $new_array;
131 if (get_magic_quotes_gpc())
133 $string = stripslashes($string);
136 if ($this->use_xss_clean === true)
138 $string = $this->xss_clean($string);
141 return preg_replace("/\015\012|\015|\012/", "\n", $string);
144 protected function _clean_input_keys($string)
146 if (!preg_match("/^[a-z0-9:_\/-]+$/i", $string))
148 exit('Disallowed characters in the array key.');
151 return $string;
154 public function get($index = '', $xss_clean = false)
156 if (!isset($_GET[$index]))
158 return false;
161 if ($xss_clean === true)
163 if (is_array($_GET[$index]))
165 foreach ($_GET[$index] as $key => $val)
167 $_GET[$index][$key] = $this->xss_clean($val);
170 else
172 return $this->xss_clean($_GET[$index]);
176 return $_GET[$index];
179 public function post($index = '', $xss_clean = false)
181 if (!isset($_POST[$index]))
183 return false;
186 if ($xss_clean === true)
188 if (is_array($_POST[$index]))
190 foreach ($_POST[$index] as $key => $val)
192 $_POST[$index][$key] = $this->xss_clean($val);
195 else
197 return $this->xss_clean($_POST[$index]);
201 return $_POST[$index];
204 public function cookie($index = '', $xss_clean = false)
206 if (!isset($_COOKIE[$index]))
208 return false;
211 if ($xss_clean === true)
213 if (is_array($_COOKIE[$index]))
215 $cookie = array();
217 foreach ($_COOKIE[$index] as $key => $val)
219 $cookie[$key] = $this->xss_clean($val);
222 return $cookie;
224 else
226 return $this->xss_clean($_COOKIE[$index]);
229 else
231 return $_COOKIE[$index];
235 public function server($index = '', $xss_clean = false)
237 if (!isset($_SERVER[$index]))
239 return false;
242 if ($xss_clean === true)
244 return $this->xss_clean($_SERVER[$index]);
247 return $_SERVER[$index];
250 public function ip_address()
252 if ($this->ip_address !== false)
254 return $this->ip_address;
257 if ($this->server('REMOTE_ADDR') && $this->server('HTTP_CLIENT_IP'))
259 $this->ip_address = $_SERVER['HTTP_CLIENT_IP'];
261 else if ($this->server('REMOTE_ADDR'))
263 $this->ip_address = $_SERVER['REMOTE_ADDR'];
265 else if ($this->server('HTTP_CLIENT_IP'))
267 $this->ip_address = $_SERVER['HTTP_CLIENT_IP'];
269 else if ($this->server('HTTP_X_FORWARDED_FOR'))
271 $this->ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'];
274 if ($this->ip_address === false)
276 $this->ip_address = '0.0.0.0';
278 return $this->ip_address;
281 if (strstr($this->ip_address, ','))
283 $x = explode(',', $this->ip_address);
284 $this->ip_address = end($x);
287 if (!$this->valid_ip($this->ip_address))
289 $this->ip_address = '0.0.0.0';
292 return $this->ip_address;
295 public function valid_ip($ip)
297 $ip_segments = explode('.', $ip);
299 if (count($ip_segments) != 4)
301 return false;
304 if (substr($ip_segments[0], 0, 1) == '0')
306 return false;
309 foreach ($ip_segments as $segment)
311 if (preg_match("/[^0-9]/", $segment) || $segment > 255 || strlen($segment) > 3)
313 return false;
317 return true;
320 public function user_agent()
322 if ($this->user_agent !== false)
324 return $this->user_agent;
327 $this->user_agent = (!isset($_SERVER['HTTP_USER_AGENT'])) ? false : $_SERVER['HTTP_USER_AGENT'];
329 return $this->user_agent;
332 public function filename_security($string)
334 $bad = array(
335 "../",
336 "./",
337 "<!--",
338 "-->",
339 "<",
340 ">",
341 "'",
342 '"',
343 '&',
344 '$',
345 '#',
346 '{',
347 '}',
348 '[',
349 ']',
350 '=',
351 ';',
352 '?',
353 "%20",
354 "%22",
355 "%3c",
356 "%253c",
357 "%3e",
358 "%0e",
359 "%28",
360 "%29",
361 "%2528",
362 "%26",
363 "%24",
364 "%3f",
365 "%3b",
366 "%3d"
369 return stripslashes(str_replace($bad, '', $string));
372 public function xss_clean($string)
374 $string = preg_replace('/\0+/', '', $string);
375 $string = preg_replace('/(\\\\0)+/', '', $string);
377 $string = preg_replace('#(&\#?[0-9a-z]+)[\x00-\x20]*;?#i', "\\1;", $string);
379 $string = preg_replace('#(&\#x?)([0-9A-F]+);?#i', "\\1\\2;", $string);
381 $string = preg_replace("/(%20)+/", '9u3iovBnRThju941s89rKozm', $string);
382 $string = preg_replace("/%u0([a-z0-9]{3})/i", "&#x\\1;", $string);
383 $string = preg_replace("/%([a-z0-9]{2})/i", "&#x\\1;", $string);
384 $string = str_replace('9u3iovBnRThju941s89rKozm', "%20", $string);
386 $string = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", array($this, '_attribute_conversion'), $string);
387 $string = preg_replace_callback("/<([\w]+)[^>]*>/si", array($this, '_html_entity_decode_callback'), $string);
389 $string = str_replace("\t", " ", $string);
391 $bad = array(
392 'document.cookie' => '[removed]',
393 'document.write' => '[removed]',
394 '.parentNode' => '[removed]',
395 '.innerHTML' => '[removed]',
396 'window.location' => '[removed]',
397 '-moz-binding' => '[removed]',
398 '<!--' => '&lt;!--',
399 '-->' => '--&gt;',
400 '<!CDATA[' => '&lt;![CDATA['
403 foreach ($bad as $key => $val)
405 $string = str_replace($key, $val, $string);
408 $bad = array(
409 "javascript\s*:" => '[removed]',
410 "expression\s*\(" => '[removed]',
411 "Redirect\s+302" => '[removed]'
414 foreach ($bad as $key => $val)
416 $string = preg_replace("#" . $key . "#i", $val, $string);
419 $string = str_replace(array('<?php', '<?PHP', '<?', '?' . '>'), array('&lt;?php', '&lt;?PHP', '&lt;?', '?&gt;'), $string);
421 $words = array('javascript', 'expression', 'vbscript', 'script', 'applet', 'alert', 'document', 'write', 'cookie', 'window');
423 foreach ($words as $word)
425 $temp = '';
427 for ($i = 0; $i < strlen($word); $i++)
429 $temp .= substr($word, $i, 1) . "\s*";
432 $string = preg_replace('#(' . substr($temp, 0, -3) . ')(\W)#ise', "preg_replace('/\s+/s', '', '\\1') . '\\2'", $string);
437 $original = $string;
439 if (stripos($string, '</a>') !== false || preg_match("/<\/a>/i", $string))
441 $string = preg_replace_callback("#<a.*?</a>#si", array($this, '_js_link_removal'), $string);
444 if (stripos($string, '<img') !== false || preg_match("/img/i", $string))
446 $string = preg_replace_callback("#<img.*?" . ">#si", array($this, '_js_img_removal'), $string);
449 if (stripos($string, 'script') !== false || stripos($string, 'xss') !== false || preg_match("/(script|xss)/i", $string))
451 $string = preg_replace("#</*(script|xss).*?\>#si", "", $string);
454 while ($original != $string);
456 unset($original);
458 $event_handlers = array('onblur', 'onchange', 'onclick', 'onfocus', 'onload', 'onmouseover', 'onmouseup', 'onmousedown', 'onselect', 'onsubmit', 'onupload', 'onkeypress', 'onkeydown', 'onkeyup', 'onresize', 'xmlns');
459 $string = preg_replace("#<([^>]+)(" . implode('|', $event_handlers) . ")([^>]*)>#iU", "&lt;\\1\\2\\3&gt;", $string);
461 $string = preg_replace('#<(/*\s*)(alert|applet|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|layer|link|meta|object|plaintext|style|script|textarea|title|xml|xss)([^>]*)>#is', "&lt;\\1\\2\\3&gt;", $string);
463 $string = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', "\\1\\2&#40;\\3&#41;", $string);
465 $bad = array(
466 'document.cookie' => '[removed]',
467 'document.write' => '[removed]',
468 '.parentNode' => '[removed]',
469 '.innerHTML' => '[removed]',
470 'window.location' => '[removed]',
471 '-moz-binding' => '[removed]',
472 '<!--' => '&lt;!--',
473 '-->' => '--&gt;',
474 '<!CDATA[' => '&lt;![CDATA['
477 foreach ($bad as $key => $val)
479 $string = str_replace($key, $val, $string);
482 $bad = array(
483 "javascript\s*:" => '[removed]',
484 "expression\s*\(" => '[removed]',
485 "Redirect\s+302" => '[removed]'
488 foreach ($bad as $key => $val)
490 $string = preg_replace("#" . $key . "#i", $val, $string);
493 log_message('debug', 'XSS filtering completed');
495 return $string;
498 protected function _js_link_removal($match)
500 return preg_replace("#<a.+?href=.*?(alert\(|alert&\#40;|javascript\:|window\.|document\.|\.cookie|<script|<xss).*?\>.*?</a>#si", "", $match[0]);
503 protected function _js_img_removal($match)
505 return preg_replace("#<img.+?src=.*?(alert\(|alert&\#40;|javascript\:|window\.|document\.|\.cookie|<script|<xss).*?\>#si", "", $match[0]);
508 protected function _attribute_conversion($match)
510 return str_replace('>', '&lt;', $match[0]);
513 protected function _html_entity_decode_callback($match)
515 $config =& load_class('Config');
517 $charset = $config->get_config_item('charset');
519 return $this->_html_entity_decode($match[0], strtoupper($charset));
522 protected function _html_entity_decode($string, $charset = 'UTF-8')
524 if (stristr($string, '&') === false)
526 return $string;
529 if (function_exists('html_entitiy_decode') && strtolower($charset) != 'utf-8')
531 $string = html_entity_decode($string, ENT_COMPAT, $charset);
532 $string = preg_match('~&#x([0-9a-f]{2,5})~ei', 'chr(hexdec("\\1"))', $string);
534 return preg_replace('~&#([0-9]{2,4})~e', 'chr(\\1)', $string);
537 $string = preg_replace('~&#x([0-9a-f]{2,5});{0,1}~ei', 'chr(hexdec("\\1"))', $string);
538 $string = preg_replace('~&#([0-9]{2,4})~e', 'chr(\\1)', $string);
540 if (stristr($string, '&') === false)
542 $string = strtr($string, array(get_html_translation_table(HTML_ENTITIES)));
545 return $string;