diff options
26 files changed, 285 insertions, 194 deletions
diff --git a/.travis.yml b/.travis.yml index 4f560442b..258ad76f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ php: - 5.4 - 5.5 - 5.6 + - 7 - hhvm env: @@ -31,11 +32,14 @@ matrix: allow_failures: - php: 5.2 - php: hhvm + - php: 7 exclude: - php: hhvm env: DB=pgsql - php: hhvm env: DB=pdo/pgsql + - php: 7 + env: DB=mysql - php: 5.2 env: DB=sqlite - php: 5.2 diff --git a/application/config/config.php b/application/config/config.php index 7d5c24c84..cc1307ca9 100644 --- a/application/config/config.php +++ b/application/config/config.php @@ -203,7 +203,7 @@ $config['directory_trigger'] = 'd'; | 3 = Informational Messages | 4 = All Messages | -| You can also pass in a array with threshold levels to show individual error types +| You can also pass an array with threshold levels to show individual error types | | array(2) = Debug Messages, without Error Messages | @@ -404,6 +404,9 @@ $config['standardize_newlines'] = FALSE; | Determines whether the XSS filter is always active when GET, POST or | COOKIE data is encountered | +| WARNING: This feature is DEPRECATED and currently available only +| for backwards compatibility purposes! +| */ $config['global_xss_filtering'] = FALSE; diff --git a/system/core/Common.php b/system/core/Common.php index 7035c18ff..ee5a705b2 100644 --- a/system/core/Common.php +++ b/system/core/Common.php @@ -497,59 +497,58 @@ if ( ! function_exists('set_status_header')) return; } - $stati = array( - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - - 400 => 'Bad Request', - 401 => 'Unauthorized', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 422 => 'Unprocessable Entity', - - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported' - ); - if (empty($code) OR ! is_numeric($code)) { show_error('Status codes must be numeric', 500); } - is_int($code) OR $code = (int) $code; - if (empty($text)) { + is_int($code) OR $code = (int) $code; + $stati = array( + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + + 400 => 'Bad Request', + 401 => 'Unauthorized', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 422 => 'Unprocessable Entity', + + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported' + ); + if (isset($stati[$code])) { $text = $stati[$code]; diff --git a/system/core/Config.php b/system/core/Config.php index a191a7727..b9af8e3b2 100644 --- a/system/core/Config.php +++ b/system/core/Config.php @@ -126,7 +126,6 @@ class CI_Config { foreach (array($file, ENVIRONMENT.'/'.$file) as $location) { $file_path = $path.'config/'.$location.'.php'; - if (in_array($file_path, $this->is_loaded, TRUE)) { return TRUE; @@ -165,14 +164,13 @@ class CI_Config { $loaded = TRUE; log_message('debug', 'Config file loaded: '.$file_path); } - - if ($loaded === TRUE) - { - return TRUE; - } } - if ($fail_gracefully === TRUE) + if ($loaded === TRUE) + { + return TRUE; + } + elseif ($fail_gracefully === TRUE) { return FALSE; } diff --git a/system/core/Input.php b/system/core/Input.php index fae3b6c08..6be4b9a6c 100644 --- a/system/core/Input.php +++ b/system/core/Input.php @@ -55,7 +55,7 @@ class CI_Input { * * @var string */ - public $ip_address = FALSE; + protected $ip_address = FALSE; /** * Allow GET array flag @@ -104,14 +104,28 @@ class CI_Input { protected $headers = array(); /** - * Input stream data + * Raw input stream data + * + * Holds a cache of php://input contents + * + * @var string + */ + protected $_raw_input_stream; + + /** + * Parsed input stream data * * Parsed from php://input at runtime * * @see CI_Input::input_stream() * @var array */ - protected $_input_stream = NULL; + protected $_input_stream; + + protected $security; + protected $uni; + + // -------------------------------------------------------------------- /** * Class constructor @@ -313,7 +327,8 @@ class CI_Input { // so we'll need to check if we have already done that first. if ( ! is_array($this->_input_stream)) { - parse_str(file_get_contents('php://input'), $this->_input_stream); + // $this->raw_input_stream will trigger __get(). + parse_str($this->raw_input_stream, $this->_input_stream); is_array($this->_input_stream) OR $this->_input_stream = array(); } @@ -846,4 +861,27 @@ class CI_Input { : strtolower($this->server('REQUEST_METHOD')); } + // ------------------------------------------------------------------------ + + /** + * Magic __get() + * + * Allows read access to protected properties + * + * @param string $name + * @return mixed + */ + public function __get($name) + { + if ($name === 'raw_input_stream') + { + isset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input'); + return $this->_raw_input_stream; + } + elseif ($name === 'ip_address') + { + return $this->ip_address; + } + } + } diff --git a/system/core/Log.php b/system/core/Log.php index 833316273..e8cb401f5 100644 --- a/system/core/Log.php +++ b/system/core/Log.php @@ -70,13 +70,6 @@ class CI_Log { protected $_threshold = 1; /** - * Highest level of logging - * - * @var int - */ - protected $_threshold_max = 0; - - /** * Array of threshold levels to log * * @var array @@ -139,7 +132,7 @@ class CI_Log { } elseif (is_array($config['log_threshold'])) { - $this->_threshold = $this->_threshold_max; + $this->_threshold = 0; $this->_threshold_array = array_flip($config['log_threshold']); } diff --git a/system/libraries/Cache/drivers/Cache_redis.php b/system/libraries/Cache/drivers/Cache_redis.php index f2a41cc67..5236556d9 100644 --- a/system/libraries/Cache/drivers/Cache_redis.php +++ b/system/libraries/Cache/drivers/Cache_redis.php @@ -243,15 +243,13 @@ class CI_Cache_redis extends CI_Driver */ public function is_supported() { - if (extension_loaded('redis')) - { - return $this->_setup_redis(); - } - else + if ( ! extension_loaded('redis')) { log_message('debug', 'The Redis extension must be loaded to use Redis cache.'); return FALSE; } + + return $this->_setup_redis(); } // ------------------------------------------------------------------------ diff --git a/system/libraries/Cache/drivers/Cache_wincache.php b/system/libraries/Cache/drivers/Cache_wincache.php index 528b2b9bf..9cc6ff016 100644 --- a/system/libraries/Cache/drivers/Cache_wincache.php +++ b/system/libraries/Cache/drivers/Cache_wincache.php @@ -194,7 +194,7 @@ class CI_Cache_wincache extends CI_Driver { */ public function is_supported() { - if ( ! extension_loaded('wincache')) + if ( ! extension_loaded('wincache') OR ! ini_get('wincache.ucenabled')) { log_message('debug', 'The Wincache PHP extension must be loaded to use Wincache Cache.'); return FALSE; diff --git a/system/libraries/Session/drivers/Session_database_driver.php b/system/libraries/Session/drivers/Session_database_driver.php index f496b4fe0..76c1cf34e 100644 --- a/system/libraries/Session/drivers/Session_database_driver.php +++ b/system/libraries/Session/drivers/Session_database_driver.php @@ -319,7 +319,7 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan if ($this->_platform === 'mysql') { $arg = $session_id.($this->_config['match_ip'] ? '_'.$_SERVER['REMOTE_ADDR'] : ''); - if ($this->_db->query("SELECT GET_LOCK('".$arg."', 10) AS ci_session_lock")->row()->ci_session_lock) + if ($this->_db->query("SELECT GET_LOCK('".$arg."', 300) AS ci_session_lock")->row()->ci_session_lock) { $this->_lock = $arg; return TRUE; diff --git a/system/libraries/Session/drivers/Session_files_driver.php b/system/libraries/Session/drivers/Session_files_driver.php index 5852277e8..74528e9d2 100644 --- a/system/libraries/Session/drivers/Session_files_driver.php +++ b/system/libraries/Session/drivers/Session_files_driver.php @@ -299,7 +299,9 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle { if ($this->close()) { - return unlink($this->_file_path.$session_id) && $this->_cookie_destroy(); + return file_exists($this->_file_path.$session_id) + ? (unlink($this->_file_path.$session_id) && $this->_cookie_destroy()) + : TRUE; } elseif ($this->_file_path !== NULL) { diff --git a/system/libraries/Session/drivers/Session_memcached_driver.php b/system/libraries/Session/drivers/Session_memcached_driver.php index f1a6e2400..938a612d9 100644 --- a/system/libraries/Session/drivers/Session_memcached_driver.php +++ b/system/libraries/Session/drivers/Session_memcached_driver.php @@ -204,7 +204,7 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa if (isset($this->_lock_key)) { - $this->_memcached->replace($this->_lock_key, time(), 5); + $this->_memcached->replace($this->_lock_key, time(), 300); if ($this->_fingerprint !== ($fingerprint = md5($session_data))) { if ($this->_memcached->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration'])) @@ -299,34 +299,21 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa { if (isset($this->_lock_key)) { - return $this->_memcached->replace($this->_lock_key, time(), 5); + return $this->_memcached->replace($this->_lock_key, time(), 300); } + // 30 attempts to obtain a lock, in case another request already has it $lock_key = $this->_key_prefix.$session_id.':lock'; - if ( ! ($ts = $this->_memcached->get($lock_key))) - { - if ( ! $this->_memcached->set($lock_key, TRUE, 5)) - { - log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); - return FALSE; - } - - $this->_lock_key = $lock_key; - $this->_lock = TRUE; - return TRUE; - } - - // Another process has the lock, we'll try to wait for it to free itself ... $attempt = 0; - while ($attempt++ < 5) + do { - usleep(((time() - $ts) * 1000000) - 20000); - if (($ts = $this->_memcached->get($lock_key)) < time()) + if ($this->_memcached->get($lock_key)) { + sleep(1); continue; } - if ( ! $this->_memcached->set($lock_key, time(), 5)) + if ( ! $this->_memcached->set($lock_key, time(), 300)) { log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); return FALSE; @@ -335,8 +322,9 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa $this->_lock_key = $lock_key; break; } + while ($attempt++ < 30); - if ($attempt === 5) + if ($attempt === 30) { log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 5 attempts, aborting.'); return FALSE; diff --git a/system/libraries/Session/drivers/Session_redis_driver.php b/system/libraries/Session/drivers/Session_redis_driver.php index 1cc4d75d7..1ce101daf 100644 --- a/system/libraries/Session/drivers/Session_redis_driver.php +++ b/system/libraries/Session/drivers/Session_redis_driver.php @@ -205,7 +205,7 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle if (isset($this->_lock_key)) { - $this->_redis->setTimeout($this->_lock_key, 5); + $this->_redis->setTimeout($this->_lock_key, 300); if ($this->_fingerprint !== ($fingerprint = md5($session_data))) { if ($this->_redis->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration'])) @@ -272,7 +272,7 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle { if (isset($this->_redis, $this->_lock_key)) { - if ($this->_redis->delete($this->_key_prefix.$session_id) !== 1) + if (($result = $this->_redis->delete($this->_key_prefix.$session_id)) !== 1) { log_message('debug', 'Session: Redis::delete() expected to return 1, got '.var_export($result, TRUE).' instead.'); } @@ -313,40 +313,21 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle { if (isset($this->_lock_key)) { - return $this->_redis->setTimeout($this->_lock_key, 5); + return $this->_redis->setTimeout($this->_lock_key, 300); } + // 30 attempts to obtain a lock, in case another request already has it $lock_key = $this->_key_prefix.$session_id.':lock'; - if (($ttl = $this->_redis->ttl($lock_key)) < 1) - { - if ( ! $this->_redis->setex($lock_key, 5, time())) - { - log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); - return FALSE; - } - - $this->_lock_key = $lock_key; - - if ($ttl === -1) - { - log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.'); - } - - $this->_lock = TRUE; - return TRUE; - } - - // Another process has the lock, we'll try to wait for it to free itself ... $attempt = 0; - while ($attempt++ < 5) + do { - usleep(($ttl * 1000000) - 20000); if (($ttl = $this->_redis->ttl($lock_key)) > 0) { + sleep(1); continue; } - if ( ! $this->_redis->setex($lock_key, 5, time())) + if ( ! $this->_redis->setex($lock_key, 300, time())) { log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); return FALSE; @@ -355,12 +336,17 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle $this->_lock_key = $lock_key; break; } + while ($attempt++ < 30); - if ($attempt === 5) + if ($attempt === 30) { - log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 5 attempts, aborting.'); + log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.'); return FALSE; } + elseif ($ttl === -1) + { + log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.'); + } $this->_lock = TRUE; return TRUE; diff --git a/tests/codeigniter/core/Security_test.php b/tests/codeigniter/core/Security_test.php index d967613b5..c96eecf02 100644 --- a/tests/codeigniter/core/Security_test.php +++ b/tests/codeigniter/core/Security_test.php @@ -126,5 +126,36 @@ class Security_test extends CI_TestCase { $this->assertEquals('foo', $safe_filename); } + + // -------------------------------------------------------------------- + public function test_strip_image_tags() + { + $imgtags = Array( + '<img src="smiley.gif" alt="Smiley face" height="42" width="42">', + '<img alt="Smiley face" height="42" width="42" src="smiley.gif">', + '<img src="http://www.w3schools.com/images/w3schools_green.jpg">', + '<img src="/img/sunset.gif" height="100%" width="100%">', + '<img src="mdn-logo-sm.png" alt="MD Logo" srcset="mdn-logo-HD.png 2x, mdn-logo-small.png 15w, mdn-banner-HD.png 100w 2x" />', + '<img sqrc="/img/sunset.gif" height="100%" width="100%">', + '<img srqc="/img/sunset.gif" height="100%" width="100%">', + '<img srcq="/img/sunset.gif" height="100%" width="100%">' + ); + + $urls = Array( + 'smiley.gif', + 'smiley.gif', + 'http://www.w3schools.com/images/w3schools_green.jpg', + '/img/sunset.gif', + 'mdn-logo-sm.png', + '<img sqrc="/img/sunset.gif" height="100%" width="100%">', + '<img srqc="/img/sunset.gif" height="100%" width="100%">', + '<img srcq="/img/sunset.gif" height="100%" width="100%">' + ); + + for($i = 0; $i < count($imgtags); $i++) + { + $this->assertEquals($urls[$i], $this->security->strip_image_tags($imgtags[$i])); + } + } }
\ No newline at end of file diff --git a/tests/codeigniter/database/DB_driver_test.php b/tests/codeigniter/database/DB_driver_test.php index c04c42b09..26416d3fc 100644 --- a/tests/codeigniter/database/DB_driver_test.php +++ b/tests/codeigniter/database/DB_driver_test.php @@ -6,7 +6,7 @@ class DB_driver_test extends CI_TestCase { { $config = Mock_Database_DB::config(DB_DRIVER); sscanf(DB_DRIVER, '%[^/]/', $driver_name); - $driver = $this->$driver_name($config[DB_DRIVER]); + $driver = $this->{$driver_name}($config[DB_DRIVER]); $this->assertTrue($driver->initialize()); } diff --git a/tests/codeigniter/database/DB_test.php b/tests/codeigniter/database/DB_test.php index d5a9369e6..dc4fae986 100644 --- a/tests/codeigniter/database/DB_test.php +++ b/tests/codeigniter/database/DB_test.php @@ -26,6 +26,14 @@ class DB_test extends CI_TestCase { { $config = Mock_Database_DB::config(DB_DRIVER); $connection = new Mock_Database_DB($config); + + // E_DEPRECATED notices thrown by mysql_connect(), mysql_pconnect() + // on PHP 5.5+ cause the tests to fail + if (DB_DRIVER === 'mysql' && version_compare(PHP_VERSION, '5.5', '>=')) + { + error_reporting(E_ALL & ~E_DEPRECATED); + } + $db = Mock_Database_DB::DB($connection->set_dsn(DB_DRIVER), TRUE); $this->assertTrue($db instanceof CI_DB); diff --git a/tests/mocks/core/input.php b/tests/mocks/core/input.php index 0d1873849..40e27441f 100644 --- a/tests/mocks/core/input.php +++ b/tests/mocks/core/input.php @@ -38,4 +38,12 @@ class Mock_Core_Input extends CI_Input { return FALSE; } + public function __set($name, $value) + { + if ($name === 'ip_address') + { + $this->ip_address = $value; + } + } + }
\ No newline at end of file diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst index 8f77f368f..ef3d2af39 100644 --- a/user_guide_src/source/changelog.rst +++ b/user_guide_src/source/changelog.rst @@ -480,6 +480,7 @@ Release Date: Not Released - :doc:`Input Library <libraries/input>` changes include: + - Deprecated the ``$config['global_xss_filtering']`` setting. - Added ``method()`` to retrieve ``$_SERVER['REQUEST_METHOD']``. - Added support for arrays and network addresses (e.g. 192.168.1.1/24) for use with the *proxy_ips* setting. - Added method ``input_stream()`` to aid in using **php://input** stream data such as one passed via PUT, DELETE and PATCH requests. @@ -493,6 +494,7 @@ Release Date: Not Released - Added an option for ``_clean_input_keys()`` to return FALSE instead of terminating the whole script. - Deprecated the ``is_cli_request()`` method, it is now an alias for the new :php:func:`is_cli()` common function. - Added an ``$xss_clean`` parameter to method ``user_agent()`` and removed the ``$user_agent`` property. + - Added property ``$raw_input_stream`` to access **php://input** data. - :doc:`Common functions <general/common_functions>` changes include: @@ -768,7 +770,7 @@ Bug fixes for 3.0 - Fixed a bug (#3161) - :doc:`Cache Library <libraries/caching>` methods `increment()`, `decrement()` didn't auto-create non-existent items when using redis and/or file storage. - Fixed a bug (#3189) - :doc:`Parser Library <libraries/parser>` used double replacement on ``key->value`` pairs, exposing a potential template injection vulnerability. - Fixed a bug (#3573) - :doc:`Email Library <libraries/email>` violated `RFC5321 <https://tools.ietf.org/rfc/rfc5321.txt>`_ by sending 'localhost.localdomain' as a hostname. -- Fixed a bug (#3572) - :doc:`CI_Security::_remove_evil_attributes()` failed for large-sized inputs due to *pcre.backtrack_limit* and didn't properly match HTML tags. +- Fixed a bug (#3572) - ``CI_Security::_remove_evil_attributes()`` failed for large-sized inputs due to *pcre.backtrack_limit* and didn't properly match HTML tags. Version 2.2.1 ============= diff --git a/user_guide_src/source/database/query_builder.rst b/user_guide_src/source/database/query_builder.rst index fa1e90353..9b4694710 100644 --- a/user_guide_src/source/database/query_builder.rst +++ b/user_guide_src/source/database/query_builder.rst @@ -1221,7 +1221,7 @@ Class Reference :param string $key: The field to search :param array $values: The values searched on - :param boolean $escape: Whether to escape values and identifiers + :param boolean $escape: Whether to escape identifiers :returns: DB_query_builder instance :rtype: object @@ -1232,7 +1232,7 @@ Class Reference :param string $key: The field to search :param array $values: The values searched on - :param boolean $escape: Whether to escape values and identifiers + :param boolean $escape: Whether to escape identifiers :returns: DB_query_builder instance :rtype: object @@ -1243,7 +1243,7 @@ Class Reference :param string $key: Name of field to examine :param array $values: Array of target values - :param boolean $escape: Whether to escape values and identifiers + :param boolean $escape: Whether to escape identifiers :returns: DB_query_builder instance :rtype: object @@ -1254,7 +1254,7 @@ Class Reference :param string $key: Name of field to examine :param array $values: Array of target values - :param boolean $escape: Whether to escape values and identifiers + :param boolean $escape: Whether to escape identifiers :returns: DB_query_builder instance :rtype: object diff --git a/user_guide_src/source/database/results.rst b/user_guide_src/source/database/results.rst index a22c2e8c3..ac44566d3 100644 --- a/user_guide_src/source/database/results.rst +++ b/user_guide_src/source/database/results.rst @@ -102,7 +102,7 @@ You can also add a second String parameter, which is the name of a class to instantiate the row with:: $query = $this->db->query("SELECT * FROM users LIMIT 1;"); - $query->row(0, 'User'); + $row = $query->row(0, 'User'); echo $row->name; // access attributes echo $row->reverse_name(); // or methods defined on the 'User' class @@ -431,4 +431,4 @@ Class Reference :rtype: array Returns an array containing the field names in the - result set.
\ No newline at end of file + result set. diff --git a/user_guide_src/source/general/security.rst b/user_guide_src/source/general/security.rst index 0c58f96b4..efc821f2b 100644 --- a/user_guide_src/source/general/security.rst +++ b/user_guide_src/source/general/security.rst @@ -133,6 +133,10 @@ with that. Please read below. provides them for you as long as you're running at least PHP version 5.3.7 (and if you don't meet that requirement - please, upgrade). + If you're one of the really unlucky people who can't even upgrade to a + more recent PHP version, use `hash_pbkdf() <http://php.net/hash_pbkdf2>`, + which we also provide in our compatibility layer. + - DO NOT ever display or send a password in plain-text format! Even to the password's owner, if you need a "Forgotten password" diff --git a/user_guide_src/source/installation/upgrade_300.rst b/user_guide_src/source/installation/upgrade_300.rst index 73ed0f4c3..2f806cccf 100644 --- a/user_guide_src/source/installation/upgrade_300.rst +++ b/user_guide_src/source/installation/upgrade_300.rst @@ -1,5 +1,5 @@ ############################# -Upgrading from 2.2.1 to 3.0.0 +Upgrading from 2.2.x to 3.0.0 ############################# .. note:: These upgrade notes are for a version that is yet to be released. @@ -551,6 +551,22 @@ PHP's native ``hash()`` function. It is deprecated and scheduled for removal in .. note:: This function is still available, but you're strongly encouraged to remove its usage sooner rather than later. +The $config['global_xss_filtering'] setting +=========================================== + +As already explained above, XSS filtering should not be done on input data, +but on output instead. Therefore, the ``$config['global_xss_filtering']``, +which automatically filters *input* data, is considered a bad practice and +is now deprecated. + +Instead, you should manually escape any user-provided data via the +:php:func:`xss_clean()` function when you need to output it, or use a +library like `HTML Purifier <http://htmlpurifier.org/>`_ that does that +for you. + +.. note:: The setting is still available, but you're strongly encouraged to + remove its usage sooner rather than later. + File helper read_file() ======================= @@ -795,7 +811,7 @@ It is now deprecated and scheduled for removal in CodeIgniter 3.1+. sooner rather than later. *********************************************************** -Step 18: Check your usage of Text helper highlight_phrase() +Step 20: Check your usage of Text helper highlight_phrase() *********************************************************** The default HTML tag used by :doc:`Text Helper <../helpers/text_helper>` function diff --git a/user_guide_src/source/installation/upgrading.rst b/user_guide_src/source/installation/upgrading.rst index ab36e9bfd..89e90e714 100644 --- a/user_guide_src/source/installation/upgrading.rst +++ b/user_guide_src/source/installation/upgrading.rst @@ -8,7 +8,7 @@ upgrading from. .. toctree:: :titlesonly: - Upgrading from 2.2.1 to 3.0.0 <upgrade_300> + Upgrading from 2.2.x to 3.0.0 <upgrade_300> Upgrading from 2.2.0 to 2.2.1 <upgrade_221> Upgrading from 2.1.4 to 2.2.0 <upgrade_220> Upgrading from 2.1.3 to 2.1.4 <upgrade_214> diff --git a/user_guide_src/source/libraries/input.rst b/user_guide_src/source/libraries/input.rst index 967f69d13..d9c6c2dd1 100644 --- a/user_guide_src/source/libraries/input.rst +++ b/user_guide_src/source/libraries/input.rst @@ -53,6 +53,10 @@ this:: Please refer to the :doc:`Security class <security>` documentation for information on using XSS Filtering in your application. +.. important:: The 'global_xss_filtering' setting is DEPRECATED and kept + solely for backwards-compatibility purposes. XSS escaping should + be performed on *output*, not *input*! + ******************* Accessing form data ******************* @@ -91,8 +95,14 @@ the ``$_POST`` array, because it will always exist and you can try and access multiple variables without caring that you might only have one shot at all of the POST data. -CodeIgniter will take care of that for you, and you can access data -from the **php://input** stream at any time, just by calling the +CodeIgniter will take care of that for you, and you can read the data +from the **php://input** stream at any time, just by using the +``$raw_input_stream`` property:: + + $this->input->raw_input_stream; + +Additionally if the input stream is form-encoded like $_POST you can +access its values by calling the ``input_stream()`` method:: $this->input->input_stream('key'); @@ -114,6 +124,12 @@ Class Reference .. php:class:: CI_Input + .. attribute:: $raw_input_stream + + Read only property that will return php://input data as is. + + The property can be read multiple times. + .. php:method:: post([$index = NULL[, $xss_clean = NULL]]) :param mixed $index: POST parameter name diff --git a/user_guide_src/source/libraries/sessions.rst b/user_guide_src/source/libraries/sessions.rst index 5a1b90537..51ecc03bd 100644 --- a/user_guide_src/source/libraries/sessions.rst +++ b/user_guide_src/source/libraries/sessions.rst @@ -632,8 +632,7 @@ Redis Driver .. note:: Since Redis doesn't have a locking mechanism exposed, locks for this driver are emulated by a separate value that is kept for up - to 5 seconds. You may experience issues if your page loads take - longer than that! + to 300 seconds. Redis is a storage engine typically used for caching and popular because of its high performance, which is also probably your reason to use the @@ -670,8 +669,7 @@ Memcached Driver .. note:: Since Memcache doesn't have a locking mechanism exposed, locks for this driver are emulated by a separate value that is kept for - up to 5 seconds. You may experience issues if your page loads take - longer than that! + up to 300 seconds. The 'memcached' driver is very similar to the 'redis' one in all of its properties, except perhaps for availability, because PHP's `Memcached diff --git a/user_guide_src/source/tutorial/news_section.rst b/user_guide_src/source/tutorial/news_section.rst index 80938de32..f436b2510 100644 --- a/user_guide_src/source/tutorial/news_section.rst +++ b/user_guide_src/source/tutorial/news_section.rst @@ -151,7 +151,7 @@ and add the next piece of code. <div class="main"> <?php echo $news_item['text'] ?> </div> - <p><a href="news/<?php echo $news_item['slug'] ?>">View article</a></p> + <p><a href="<?php echo $news_item['slug'] ?>">View article</a></p> <?php endforeach ?> diff --git a/user_guide_src/source/tutorial/static_pages.rst b/user_guide_src/source/tutorial/static_pages.rst index 8ba0486c1..53f286473 100644 --- a/user_guide_src/source/tutorial/static_pages.rst +++ b/user_guide_src/source/tutorial/static_pages.rst @@ -11,12 +11,16 @@ static pages. A controller is simply a class that helps delegate work. It is the glue of your web application. For example, when a call is made to: -``http://example.com/news/latest/10`` We might imagine that there is a -controller named "news". The method being called on news would be -"latest". The news method's job could be to grab 10 news items, and -render them on the page. Very often in MVC, you'll see URL patterns that -match: -``http://example.com/[controller-class]/[controller-method]/[arguments]`` + + http://example.com/news/latest/10 + +We might imagine that there is a controller named "news". The method +being called on news would be "latest". The news method's job could be to +grab 10 news items, and render them on the page. Very often in MVC, +you'll see URL patterns that match: + + http://example.com/[controller-class]/[controller-method]/[arguments] + As URL schemes become more complex, this may change. But for now, this is all we will need to know. @@ -25,15 +29,13 @@ code. :: - <?php - class Pages extends CI_Controller { + <?php + class Pages extends CI_Controller { - public function view($page = 'home') - { - - } - - } + public function view($page = 'home') + { + } + } You have created a class named "pages", with a view method that accepts one argument named $page. The pages class is extending the @@ -56,13 +58,13 @@ following code. :: - <html> - <head> - <title>CodeIgniter Tutorial</title> - </head> - <body> + <html> + <head> + <title>CodeIgniter Tutorial</title> + </head> + <body> - <h1>CodeIgniter Tutorial</h1> + <h1>CodeIgniter Tutorial</h1> The header contains the basic HTML code that you'll want to display before loading the main view, together with a heading. It will also @@ -72,16 +74,16 @@ includes the following code: :: - <em>© 2014</em> - </body> - </html> + <em>© 2014</em> + </body> + </html> Adding logic to the controller ------------------------------ -Earlier you set up a controller with a view() method. The method accepts -one parameter, which is the name of the page to be loaded. The static -page templates will be located in the application/views/pages/ +Earlier you set up a controller with a ``view()`` method. The method +accepts one parameter, which is the name of the page to be loaded. The +static page templates will be located in the application/views/pages/ directory. In that directory, create two files named home.php and about.php. Within @@ -93,43 +95,40 @@ page actually exists: :: - <?php - public function view($page = 'home') - { - - if ( ! file_exists(APPPATH.'/views/pages/'.$page.'.php')) - { - // Whoops, we don't have a page for that! - show_404(); - } - - $data['title'] = ucfirst($page); // Capitalize the first letter - - $this->load->view('templates/header', $data); - $this->load->view('pages/'.$page, $data); - $this->load->view('templates/footer', $data); - - } + public function view($page = 'home') + { + if ( ! file_exists(APPPATH.'/views/pages/'.$page.'.php')) + { + // Whoops, we don't have a page for that! + show_404(); + } + + $data['title'] = ucfirst($page); // Capitalize the first letter + + $this->load->view('templates/header', $data); + $this->load->view('pages/'.$page, $data); + $this->load->view('templates/footer', $data); + } Now, when the page does exist, it is loaded, including the header and footer, and displayed to the user. If the page doesn't exist, a "404 Page not found" error is shown. The first line in this method checks whether the page actually exists. -PHP's native file\_exists() function is used to check whether the file -is where it's expected to be. show\_404() is a built-in CodeIgniter +PHP's native ``file_exists()`` function is used to check whether the file +is where it's expected to be. ``show_404()`` is a built-in CodeIgniter function that renders the default error page. -In the header template, the $title variable was used to customize the +In the header template, the ``$title`` variable was used to customize the page title. The value of title is defined in this method, but instead of assigning the value to a variable, it is assigned to the title element in the $data array. The last thing that has to be done is loading the views in the order -they should be displayed. The second parameter in the view() method is -used to pass values to the view. Each value in the $data array is +they should be displayed. The second parameter in the ``view()`` method is +used to pass values to the view. Each value in the ``$data`` array is assigned to a variable with the name of its key. So the value of -$data['title'] in the controller is equivalent to $title in the view. +``$data['title']`` in the controller is equivalent to $title in the view. Routing ------- @@ -149,8 +148,8 @@ all other code that sets any element in the $route array. :: - $route['default_controller'] = 'pages/view'; - $route['(:any)'] = 'pages/view/$1'; + $route['default_controller'] = 'pages/view'; + $route['(:any)'] = 'pages/view/$1'; CodeIgniter reads its routing rules from top to bottom and routes the request to the first matching rule. Each rule is a regular expression @@ -163,8 +162,8 @@ More information about routing can be found in the URI Routing `documentation <../general/routing.html>`_. Here, the second rule in the $routes array matches **any** request using -the wildcard string (:any). and passes the parameter to the view() +the wildcard string (:any). and passes the parameter to the ``view()`` method of the pages class. -Now visit index.php/about. Did it get routed correctly to the view() +Now visit index.php/about. Did it get routed correctly to the ``view()`` method in the pages controller? Awesome! |