1 <?php defined('SYSPATH') OR die('No direct access allowed.');
5 * $Id: Input.php 4346 2009-05-11 17:08:15Z zombor $
9 * @copyright (c) 2007-2008 Kohana Team
10 * @license http://kohanaphp.com/license.html
14 // Enable or disable automatic XSS cleaning
15 protected $use_xss_clean = FALSE;
17 // Are magic quotes enabled?
18 protected $magic_quotes_gpc = FALSE;
20 // IP address of current user
24 protected static $instance;
27 * Retrieve a singleton instance of Input. This will always be the first
28 * created instance of this class.
32 public static function instance()
34 if (Input::$instance === NULL)
36 // Create a new instance
40 return Input::$instance;
44 * Sanitizes global GET, POST and COOKIE data. Also takes care of
45 * magic_quotes and register_globals, if they have been enabled.
49 public function __construct()
52 $this->use_xss_clean = (bool) Kohana::config('core.global_xss_filtering');
54 if (Input::$instance === NULL)
56 // magic_quotes_runtime is enabled
57 if (get_magic_quotes_runtime())
59 set_magic_quotes_runtime(0);
60 Kohana::log('debug', 'Disable magic_quotes_runtime! It is evil and deprecated: http://php.net/magic_quotes');
63 // magic_quotes_gpc is enabled
64 if (get_magic_quotes_gpc())
66 $this->magic_quotes_gpc = TRUE;
67 Kohana::log('debug', 'Disable magic_quotes_gpc! It is evil and deprecated: http://php.net/magic_quotes');
70 // register_globals is enabled
71 if (ini_get('register_globals'))
73 if (isset($_REQUEST['GLOBALS']))
75 // Prevent GLOBALS override attacks
76 exit('Global variable overload attack.');
79 // Destroy the REQUEST global
82 // These globals are standard and should not be removed
83 $preserve = array('GLOBALS', '_REQUEST', '_GET', '_POST', '_FILES', '_COOKIE', '_SERVER', '_ENV', '_SESSION');
85 // This loop has the same effect as disabling register_globals
86 foreach (array_diff(array_keys($GLOBALS), $preserve) as $key)
91 // Unset the global variable
92 unset($GLOBALS[$key], $$key);
95 // Warn the developer about register globals
96 Kohana::log('debug', 'Disable register_globals! It is evil and deprecated: http://php.net/register_globals');
101 foreach ($_GET as $key => $val)
104 $_GET[$this->clean_input_keys($key)] = $this->clean_input_data($val);
112 if (is_array($_POST))
114 foreach ($_POST as $key => $val)
117 $_POST[$this->clean_input_keys($key)] = $this->clean_input_data($val);
125 if (is_array($_COOKIE))
127 foreach ($_COOKIE as $key => $val)
129 // Ignore special attributes in RFC2109 compliant cookies
130 if ($key == '$Version' OR $key == '$Path' OR $key == '$Domain')
134 $_COOKIE[$this->clean_input_keys($key)] = $this->clean_input_data($val);
142 // Create a singleton
143 Input::$instance = $this;
145 Kohana::log('debug', 'Global GET, POST and COOKIE data sanitized');
150 * Fetch an item from the $_GET array.
152 * @param string key to find
153 * @param mixed default value
154 * @param boolean XSS clean the value
157 public function get($key = array(), $default = NULL, $xss_clean = FALSE)
159 return $this->search_array($_GET, $key, $default, $xss_clean);
163 * Fetch an item from the $_POST array.
165 * @param string key to find
166 * @param mixed default value
167 * @param boolean XSS clean the value
170 public function post($key = array(), $default = NULL, $xss_clean = FALSE)
172 return $this->search_array($_POST, $key, $default, $xss_clean);
176 * Fetch an item from the $_COOKIE array.
178 * @param string key to find
179 * @param mixed default value
180 * @param boolean XSS clean the value
183 public function cookie($key = array(), $default = NULL, $xss_clean = FALSE)
185 return $this->search_array($_COOKIE, $key, $default, $xss_clean);
189 * Fetch an item from the $_SERVER array.
191 * @param string key to find
192 * @param mixed default value
193 * @param boolean XSS clean the value
196 public function server($key = array(), $default = NULL, $xss_clean = FALSE)
198 return $this->search_array($_SERVER, $key, $default, $xss_clean);
202 * Fetch an item from a global array.
204 * @param array array to search
205 * @param string key to find
206 * @param mixed default value
207 * @param boolean XSS clean the value
210 protected function search_array($array, $key, $default = NULL, $xss_clean = FALSE)
212 if ($key === array())
215 if ( ! isset($array[$key]))
219 $value = $array[$key];
221 if ($this->use_xss_clean === FALSE AND $xss_clean === TRUE)
223 // XSS clean the value
224 $value = $this->xss_clean($value);
231 * Fetch the IP Address.
235 public function ip_address()
237 if ($this->ip_address !== NULL)
238 return $this->ip_address;
240 // Server keys that could contain the client IP address
241 $keys = array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'REMOTE_ADDR');
243 foreach ($keys as $key)
245 if ($ip = $this->server($key))
247 $this->ip_address = $ip;
249 // An IP address has been found
254 if ($comma = strrpos($this->ip_address, ',') !== FALSE)
256 $this->ip_address = substr($this->ip_address, $comma + 1);
259 if ( ! valid::ip($this->ip_address))
262 $this->ip_address = '0.0.0.0';
265 return $this->ip_address;
269 * Clean cross site scripting exploits from string.
270 * HTMLPurifier may be used if installed, otherwise defaults to built in method.
271 * Note - This function should only be used to deal with data upon submission.
272 * It's not something that should be used for general runtime processing
273 * since it requires a fair amount of processing overhead.
275 * @param string data to clean
276 * @param string xss_clean method to use ('htmlpurifier' or defaults to built-in method)
279 public function xss_clean($data, $tool = NULL)
283 // Use the default tool
284 $tool = Kohana::config('core.global_xss_filtering');
289 foreach ($data as $key => $val)
291 $data[$key] = $this->xss_clean($val, $tool);
297 // Do not clean empty strings
298 if (trim($data) === '')
303 // NOTE: This is necessary because switch is NOT type-sensative!
311 * @todo License should go here, http://htmlpurifier.org/
313 if ( ! class_exists('HTMLPurifier_Config', FALSE))
316 require Kohana::find_file('vendor', 'htmlpurifier/HTMLPurifier.auto', TRUE);
317 require 'HTMLPurifier.func.php';
321 $config = HTMLPurifier_Config::createDefault();
322 $config->set('HTML', 'TidyLevel', 'none'); // Only XSS cleaning now
325 $data = HTMLPurifier($data, $config);
328 // http://svn.bitflux.ch/repos/public/popoon/trunk/classes/externalinput.php
329 // +----------------------------------------------------------------------+
330 // | Copyright (c) 2001-2006 Bitflux GmbH |
331 // +----------------------------------------------------------------------+
332 // | Licensed under the Apache License, Version 2.0 (the "License"); |
333 // | you may not use this file except in compliance with the License. |
334 // | You may obtain a copy of the License at |
335 // | http://www.apache.org/licenses/LICENSE-2.0 |
336 // | Unless required by applicable law or agreed to in writing, software |
337 // | distributed under the License is distributed on an "AS IS" BASIS, |
338 // | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
339 // | implied. See the License for the specific language governing |
340 // | permissions and limitations under the License. |
341 // +----------------------------------------------------------------------+
342 // | Author: Christian Stocker <chregu@bitflux.ch> |
343 // +----------------------------------------------------------------------+
345 // Kohana Modifications:
346 // * Changed double quotes to single quotes, changed indenting and spacing
347 // * Removed magic_quotes stuff
348 // * Increased regex readability:
349 // * Used delimeters that aren't found in the pattern
350 // * Removed all unneeded escapes
351 // * Deleted U modifiers and swapped greediness where needed
352 // * Increased regex speed:
353 // * Made capturing parentheses non-capturing where possible
354 // * Removed parentheses where possible
355 // * Split up alternation alternatives
356 // * Made some quantifiers possessive
359 $data = str_replace(array('&','<','>'), array('&amp;','&lt;','&gt;'), $data);
360 $data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data);
361 $data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data);
362 $data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');
364 // Remove any attribute starting with "on" or xmlns
365 $data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);
367 // Remove javascript: and vbscript: protocols
368 $data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data);
369 $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data);
370 $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data);
372 // Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
373 $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
374 $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
375 $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data);
377 // Remove namespaced elements (we do not need them)
378 $data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data);
382 // Remove really unwanted tags
384 $data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
386 while ($old_data !== $data);
394 * This is a helper method. It enforces W3C specifications for allowed
395 * key name strings, to prevent malicious exploitation.
397 * @param string string to clean
400 public function clean_input_keys($str)
402 $chars = PCRE_UNICODE_PROPERTIES ? '\pL' : 'a-zA-Z';
404 if ( ! preg_match('#^['.$chars.'0-9:_.-]++$#uD', $str))
406 exit('Disallowed key characters in global data.');
413 * This is a helper method. It escapes data and forces all newline
414 * characters to "\n".
416 * @param unknown_type string to clean
419 public function clean_input_data($str)
423 $new_array = array();
424 foreach ($str as $key => $val)
427 $new_array[$this->clean_input_keys($key)] = $this->clean_input_data($val);
432 if ($this->magic_quotes_gpc === TRUE)
434 // Remove annoying magic quotes
435 $str = stripslashes($str);
438 if ($this->use_xss_clean === TRUE)
440 $str = $this->xss_clean($str);
443 if (strpos($str, "\r") !== FALSE)
445 // Standardize newlines
446 $str = str_replace(array("\r\n", "\r"), "\n", $str);