system/core/Security.php
.htaccess
AesEncryption.php
LICENSE
application
.htaccess
cache
index.html
config
autoload.php
config.php
constants.php
database.php
doctypes.php
foreign_chars.php
hooks.php
index.html
memcached.php
migration.php
mimes.php
profiler.php
routes.php
smileys.php
user_agents.php
controllers
Systemid.php
Welcome.php
index.html
core
index.html
helpers
index.html
hooks
index.html
index.html
language
english
index.html
system_lang.php
index.html
libraries
index.html
logs
index.html
models
System_model.php
index.html
third_party
index.html
views
about.php
category
addedit.php
addeditsub.php
components.php
delete.php
errors
cli
error_404.php
error_db.php
error_exception.php
error_general.php
error_php.php
index.html
html
error_404.php
error_db.php
error_exception.php
error_general.php
error_php.php
index.html
index.html
foot.php
generator.php
head.php
index.html
login.php
main.php
pass
addedit.php
show.php
welcome_message.php
captcha
sys.txt
favicon.ico
index.php
install.php
library
AesEncryption.php
bootstrap
css
.DS_Store
bootstrap-grid.css
bootstrap-grid.css.map
bootstrap-grid.min.css
bootstrap-grid.min.css.map
bootstrap-reboot.css
bootstrap-reboot.css.map
bootstrap-reboot.min.css
bootstrap-reboot.min.css.map
bootstrap.css
bootstrap.css.map
bootstrap.min.css
bootstrap.min.css.map
js
bootstrap.bundle.js
bootstrap.bundle.js.map
bootstrap.bundle.min.js
bootstrap.bundle.min.js.map
bootstrap.js
bootstrap.js.map
bootstrap.min.js
bootstrap.min.js.map
font-awesome
HELP-US-OUT.txt
css
font-awesome.css
font-awesome.min.css
fonts
FontAwesome.otf
fontawesome-webfont.eot
fontawesome-webfont.svg
fontawesome-webfont.ttf
fontawesome-webfont.woff
fontawesome-webfont.woff2
less
animated.less
bordered-pulled.less
core.less
fixed-width.less
font-awesome.less
icons.less
larger.less
list.less
mixins.less
path.less
rotated-flipped.less
screen-reader.less
stacked.less
variables.less
scss
_animated.scss
_bordered-pulled.scss
_core.scss
_fixed-width.scss
_icons.scss
_larger.scss
_list.scss
_mixins.scss
_path.scss
_rotated-flipped.scss
_screen-reader.scss
_stacked.scss
_variables.scss
font-awesome.scss
font-awesome.min.css
jquery-ui
AUTHORS.txt
LICENSE.txt
external
jquery
jquery.js
images
ui-icons_444444_256x240.png
ui-icons_555555_256x240.png
ui-icons_777620_256x240.png
ui-icons_777777_256x240.png
ui-icons_cc0000_256x240.png
ui-icons_ffffff_256x240.png
index.html
jquery-ui.css
jquery-ui.js
jquery-ui.min.css
jquery-ui.min.js
jquery-ui.structure.css
jquery-ui.structure.min.css
jquery-ui.theme.css
jquery-ui.theme.min.css
package.json
jquery.min.js
jquerysctipttop.css
logo.png
password_indicator_bs4
css
signin.css
index.html
js
jaktutorial.js
popper.min.js
right-click
css
contextMenu.min.css
js
contextMenu.min.js
style.css
tether
css
tether-theme-arrows-dark.css
tether-theme-arrows-dark.min.css
tether-theme-arrows.css
tether-theme-arrows.min.css
tether-theme-basic.css
tether-theme-basic.min.css
tether.css
tether.min.css
js
tether.js
tether.min.js
system
.htaccess
core
Benchmark.php
CodeIgniter.php
Common.php
Config.php
Controller.php
Exceptions.php
Hooks.php
Input.php
Lang.php
Loader.php
Log.php
Model.php
Output.php
Router.php
Security.php
URI.php
Utf8.php
compat
hash.php
index.html
mbstring.php
password.php
standard.php
index.html
database
DB.php
DB_cache.php
DB_driver.php
DB_forge.php
DB_query_builder.php
DB_result.php
DB_utility.php
drivers
cubrid
cubrid_driver.php
cubrid_forge.php
cubrid_result.php
cubrid_utility.php
index.html
ibase
ibase_driver.php
ibase_forge.php
ibase_result.php
ibase_utility.php
index.html
index.html
mssql
index.html
mssql_driver.php
mssql_forge.php
mssql_result.php
mssql_utility.php
mysql
index.html
mysql_driver.php
mysql_forge.php
mysql_result.php
mysql_utility.php
mysqli
index.html
mysqli_driver.php
mysqli_forge.php
mysqli_result.php
mysqli_utility.php
oci8
index.html
oci8_driver.php
oci8_forge.php
oci8_result.php
oci8_utility.php
odbc
index.html
odbc_driver.php
odbc_forge.php
odbc_result.php
odbc_utility.php
pdo
index.html
pdo_driver.php
pdo_forge.php
pdo_result.php
pdo_utility.php
subdrivers
index.html
pdo_4d_driver.php
pdo_4d_forge.php
pdo_cubrid_driver.php
pdo_cubrid_forge.php
pdo_dblib_driver.php
pdo_dblib_forge.php
pdo_firebird_driver.php
pdo_firebird_forge.php
pdo_ibm_driver.php
pdo_ibm_forge.php
pdo_informix_driver.php
pdo_informix_forge.php
pdo_mysql_driver.php
pdo_mysql_forge.php
pdo_oci_driver.php
pdo_oci_forge.php
pdo_odbc_driver.php
pdo_odbc_forge.php
pdo_pgsql_driver.php
pdo_pgsql_forge.php
pdo_sqlite_driver.php
pdo_sqlite_forge.php
pdo_sqlsrv_driver.php
pdo_sqlsrv_forge.php
postgre
index.html
postgre_driver.php
postgre_forge.php
postgre_result.php
postgre_utility.php
sqlite
index.html
sqlite_driver.php
sqlite_forge.php
sqlite_result.php
sqlite_utility.php
sqlite3
index.html
sqlite3_driver.php
sqlite3_forge.php
sqlite3_result.php
sqlite3_utility.php
sqlsrv
index.html
sqlsrv_driver.php
sqlsrv_forge.php
sqlsrv_result.php
sqlsrv_utility.php
index.html
fonts
index.html
texb.ttf
helpers
array_helper.php
captcha_helper.php
cookie_helper.php
date_helper.php
directory_helper.php
download_helper.php
email_helper.php
file_helper.php
form_helper.php
html_helper.php
index.html
inflector_helper.php
language_helper.php
number_helper.php
path_helper.php
security_helper.php
smiley_helper.php
string_helper.php
text_helper.php
typography_helper.php
url_helper.php
xml_helper.php
index.html
language
english
calendar_lang.php
date_lang.php
db_lang.php
email_lang.php
form_validation_lang.php
ftp_lang.php
imglib_lang.php
index.html
migration_lang.php
number_lang.php
pagination_lang.php
profiler_lang.php
unit_test_lang.php
upload_lang.php
index.html
libraries
Cache
Cache.php
drivers
Cache_apc.php
Cache_dummy.php
Cache_file.php
Cache_memcached.php
Cache_redis.php
Cache_wincache.php
index.html
index.html
Calendar.php
Cart.php
Driver.php
Email.php
Encrypt.php
Encryption.php
Form_validation.php
Ftp.php
Image_lib.php
Javascript
Jquery.php
index.html
Javascript.php
Migration.php
Pagination.php
Parser.php
Profiler.php
Session
Session.php
SessionHandlerInterface.php
Session_driver.php
drivers
Session_database_driver.php
Session_files_driver.php
Session_memcached_driver.php
Session_redis_driver.php
index.html
index.html
Table.php
Trackback.php
Typography.php
Unit_test.php
Upload.php
User_agent.php
Xmlrpc.php
Xmlrpcs.php
Zip.php
index.html
AesEncryption.php
LICENSE
application
.htaccess
cache
index.html
config
autoload.php
config.php
constants.php
database.php
doctypes.php
foreign_chars.php
hooks.php
index.html
memcached.php
migration.php
mimes.php
profiler.php
routes.php
smileys.php
user_agents.php
controllers
Systemid.php
Welcome.php
index.html
core
index.html
helpers
index.html
hooks
index.html
index.html
language
english
index.html
system_lang.php
index.html
libraries
index.html
logs
index.html
models
System_model.php
index.html
third_party
index.html
views
about.php
category
addedit.php
addeditsub.php
components.php
delete.php
errors
cli
error_404.php
error_db.php
error_exception.php
error_general.php
error_php.php
index.html
html
error_404.php
error_db.php
error_exception.php
error_general.php
error_php.php
index.html
index.html
foot.php
generator.php
head.php
index.html
login.php
main.php
pass
addedit.php
show.php
welcome_message.php
captcha
sys.txt
favicon.ico
index.php
install.php
library
AesEncryption.php
bootstrap
css
.DS_Store
bootstrap-grid.css
bootstrap-grid.css.map
bootstrap-grid.min.css
bootstrap-grid.min.css.map
bootstrap-reboot.css
bootstrap-reboot.css.map
bootstrap-reboot.min.css
bootstrap-reboot.min.css.map
bootstrap.css
bootstrap.css.map
bootstrap.min.css
bootstrap.min.css.map
js
bootstrap.bundle.js
bootstrap.bundle.js.map
bootstrap.bundle.min.js
bootstrap.bundle.min.js.map
bootstrap.js
bootstrap.js.map
bootstrap.min.js
bootstrap.min.js.map
font-awesome
HELP-US-OUT.txt
css
font-awesome.css
font-awesome.min.css
fonts
FontAwesome.otf
fontawesome-webfont.eot
fontawesome-webfont.svg
fontawesome-webfont.ttf
fontawesome-webfont.woff
fontawesome-webfont.woff2
less
animated.less
bordered-pulled.less
core.less
fixed-width.less
font-awesome.less
icons.less
larger.less
list.less
mixins.less
path.less
rotated-flipped.less
screen-reader.less
stacked.less
variables.less
scss
_animated.scss
_bordered-pulled.scss
_core.scss
_fixed-width.scss
_icons.scss
_larger.scss
_list.scss
_mixins.scss
_path.scss
_rotated-flipped.scss
_screen-reader.scss
_stacked.scss
_variables.scss
font-awesome.scss
font-awesome.min.css
jquery-ui
AUTHORS.txt
LICENSE.txt
external
jquery
jquery.js
images
ui-icons_444444_256x240.png
ui-icons_555555_256x240.png
ui-icons_777620_256x240.png
ui-icons_777777_256x240.png
ui-icons_cc0000_256x240.png
ui-icons_ffffff_256x240.png
index.html
jquery-ui.css
jquery-ui.js
jquery-ui.min.css
jquery-ui.min.js
jquery-ui.structure.css
jquery-ui.structure.min.css
jquery-ui.theme.css
jquery-ui.theme.min.css
package.json
jquery.min.js
jquerysctipttop.css
logo.png
password_indicator_bs4
css
signin.css
index.html
js
jaktutorial.js
popper.min.js
right-click
css
contextMenu.min.css
js
contextMenu.min.js
style.css
tether
css
tether-theme-arrows-dark.css
tether-theme-arrows-dark.min.css
tether-theme-arrows.css
tether-theme-arrows.min.css
tether-theme-basic.css
tether-theme-basic.min.css
tether.css
tether.min.css
js
tether.js
tether.min.js
system
.htaccess
core
Benchmark.php
CodeIgniter.php
Common.php
Config.php
Controller.php
Exceptions.php
Hooks.php
Input.php
Lang.php
Loader.php
Log.php
Model.php
Output.php
Router.php
Security.php
URI.php
Utf8.php
compat
hash.php
index.html
mbstring.php
password.php
standard.php
index.html
database
DB.php
DB_cache.php
DB_driver.php
DB_forge.php
DB_query_builder.php
DB_result.php
DB_utility.php
drivers
cubrid
cubrid_driver.php
cubrid_forge.php
cubrid_result.php
cubrid_utility.php
index.html
ibase
ibase_driver.php
ibase_forge.php
ibase_result.php
ibase_utility.php
index.html
index.html
mssql
index.html
mssql_driver.php
mssql_forge.php
mssql_result.php
mssql_utility.php
mysql
index.html
mysql_driver.php
mysql_forge.php
mysql_result.php
mysql_utility.php
mysqli
index.html
mysqli_driver.php
mysqli_forge.php
mysqli_result.php
mysqli_utility.php
oci8
index.html
oci8_driver.php
oci8_forge.php
oci8_result.php
oci8_utility.php
odbc
index.html
odbc_driver.php
odbc_forge.php
odbc_result.php
odbc_utility.php
pdo
index.html
pdo_driver.php
pdo_forge.php
pdo_result.php
pdo_utility.php
subdrivers
index.html
pdo_4d_driver.php
pdo_4d_forge.php
pdo_cubrid_driver.php
pdo_cubrid_forge.php
pdo_dblib_driver.php
pdo_dblib_forge.php
pdo_firebird_driver.php
pdo_firebird_forge.php
pdo_ibm_driver.php
pdo_ibm_forge.php
pdo_informix_driver.php
pdo_informix_forge.php
pdo_mysql_driver.php
pdo_mysql_forge.php
pdo_oci_driver.php
pdo_oci_forge.php
pdo_odbc_driver.php
pdo_odbc_forge.php
pdo_pgsql_driver.php
pdo_pgsql_forge.php
pdo_sqlite_driver.php
pdo_sqlite_forge.php
pdo_sqlsrv_driver.php
pdo_sqlsrv_forge.php
postgre
index.html
postgre_driver.php
postgre_forge.php
postgre_result.php
postgre_utility.php
sqlite
index.html
sqlite_driver.php
sqlite_forge.php
sqlite_result.php
sqlite_utility.php
sqlite3
index.html
sqlite3_driver.php
sqlite3_forge.php
sqlite3_result.php
sqlite3_utility.php
sqlsrv
index.html
sqlsrv_driver.php
sqlsrv_forge.php
sqlsrv_result.php
sqlsrv_utility.php
index.html
fonts
index.html
texb.ttf
helpers
array_helper.php
captcha_helper.php
cookie_helper.php
date_helper.php
directory_helper.php
download_helper.php
email_helper.php
file_helper.php
form_helper.php
html_helper.php
index.html
inflector_helper.php
language_helper.php
number_helper.php
path_helper.php
security_helper.php
smiley_helper.php
string_helper.php
text_helper.php
typography_helper.php
url_helper.php
xml_helper.php
index.html
language
english
calendar_lang.php
date_lang.php
db_lang.php
email_lang.php
form_validation_lang.php
ftp_lang.php
imglib_lang.php
index.html
migration_lang.php
number_lang.php
pagination_lang.php
profiler_lang.php
unit_test_lang.php
upload_lang.php
index.html
libraries
Cache
Cache.php
drivers
Cache_apc.php
Cache_dummy.php
Cache_file.php
Cache_memcached.php
Cache_redis.php
Cache_wincache.php
index.html
index.html
Calendar.php
Cart.php
Driver.php
Email.php
Encrypt.php
Encryption.php
Form_validation.php
Ftp.php
Image_lib.php
Javascript
Jquery.php
index.html
Javascript.php
Migration.php
Pagination.php
Parser.php
Profiler.php
Session
Session.php
SessionHandlerInterface.php
Session_driver.php
drivers
Session_database_driver.php
Session_files_driver.php
Session_memcached_driver.php
Session_redis_driver.php
index.html
index.html
Table.php
Trackback.php
Typography.php
Unit_test.php
Upload.php
User_agent.php
Xmlrpc.php
Xmlrpcs.php
Zip.php
index.html
<?php
/**
* CodeIgniter
*
* An open source application development framework for PHP
*
* This content is released under the MIT License (MIT)
*
* Copyright (c) 2014 - 2018, British Columbia Institute of Technology
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @package CodeIgniter
* @author EllisLab Dev Team
* @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
* @copyright Copyright (c) 2014 - 2018, British Columbia Institute of Technology (http://bcit.ca/)
* @license http://opensource.org/licenses/MIT MIT License
* @link https://codeigniter.com
* @since Version 1.0.0
* @filesource
*/
defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Security Class
*
* @package CodeIgniter
* @subpackage Libraries
* @category Security
* @author EllisLab Dev Team
* @link https://codeigniter.com/user_guide/libraries/security.html
*/
class CI_Security {
/**
* List of sanitize filename strings
*
* @var array
*/
public $filename_bad_chars = array(
'../', '<!--', '-->', '<', '>',
"'", '"', '&', '$', '#',
'{', '}', '[', ']', '=',
';', '?', '%20', '%22',
'%3c', // <
'%253c', // <
'%3e', // >
'%0e', // >
'%28', // (
'%29', // )
'%2528', // (
'%26', // &
'%24', // $
'%3f', // ?
'%3b', // ;
'%3d' // =
);
/**
* Character set
*
* Will be overridden by the constructor.
*
* @var string
*/
public $charset = 'UTF-8';
/**
* XSS Hash
*
* Random Hash for protecting URLs.
*
* @var string
*/
protected $_xss_hash;
/**
* CSRF Hash
*
* Random hash for Cross Site Request Forgery protection cookie
*
* @var string
*/
protected $_csrf_hash;
/**
* CSRF Expire time
*
* Expiration time for Cross Site Request Forgery protection cookie.
* Defaults to two hours (in seconds).
*
* @var int
*/
protected $_csrf_expire = 7200;
/**
* CSRF Token name
*
* Token name for Cross Site Request Forgery protection cookie.
*
* @var string
*/
protected $_csrf_token_name = 'ci_csrf_token';
/**
* CSRF Cookie name
*
* Cookie name for Cross Site Request Forgery protection cookie.
*
* @var string
*/
protected $_csrf_cookie_name = 'ci_csrf_token';
/**
* List of never allowed strings
*
* @var array
*/
protected $_never_allowed_str = array(
'document.cookie' => '[removed]',
'(document).cookie' => '[removed]',
'document.write' => '[removed]',
'(document).write' => '[removed]',
'.parentNode' => '[removed]',
'.innerHTML' => '[removed]',
'-moz-binding' => '[removed]',
'<!--' => '<!--',
'-->' => '-->',
'<![CDATA[' => '<![CDATA[',
'<comment>' => '<comment>',
'<%' => '<%'
);
/**
* List of never allowed regex replacements
*
* @var array
*/
protected $_never_allowed_regex = array(
'javascript\s*:',
'(\(?document\)?|\(?window\)?(\.document)?)\.(location|on\w*)',
'expression\s*(\(|&\#40;)', // CSS and IE
'vbscript\s*:', // IE, surprise!
'wscript\s*:', // IE
'jscript\s*:', // IE
'vbs\s*:', // IE
'Redirect\s+30\d',
"([\"'])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?"
);
/**
* Class constructor
*
* @return void
*/
public function __construct()
{
// Is CSRF protection enabled?
if (config_item('csrf_protection'))
{
// CSRF config
foreach (array('csrf_expire', 'csrf_token_name', 'csrf_cookie_name') as $key)
{
if (NULL !== ($val = config_item($key)))
{
$this->{'_'.$key} = $val;
}
}
// Append application specific cookie prefix
if ($cookie_prefix = config_item('cookie_prefix'))
{
$this->_csrf_cookie_name = $cookie_prefix.$this->_csrf_cookie_name;
}
// Set the CSRF hash
$this->_csrf_set_hash();
}
$this->charset = strtoupper(config_item('charset'));
log_message('info', 'Security Class Initialized');
}
// --------------------------------------------------------------------
/**
* CSRF Verify
*
* @return CI_Security
*/
public function csrf_verify()
{
// If it's not a POST request we will set the CSRF cookie
if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST')
{
return $this->csrf_set_cookie();
}
// Check if URI has been whitelisted from CSRF checks
if ($exclude_uris = config_item('csrf_exclude_uris'))
{
$uri = load_class('URI', 'core');
foreach ($exclude_uris as $excluded)
{
if (preg_match('#^'.$excluded.'$#i'.(UTF8_ENABLED ? 'u' : ''), $uri->uri_string()))
{
return $this;
}
}
}
// Check CSRF token validity, but don't error on mismatch just yet - we'll want to regenerate
$valid = isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])
&& hash_equals($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name]);
// We kill this since we're done and we don't want to pollute the _POST array
unset($_POST[$this->_csrf_token_name]);
// Regenerate on every submission?
if (config_item('csrf_regenerate'))
{
// Nothing should last forever
unset($_COOKIE[$this->_csrf_cookie_name]);
$this->_csrf_hash = NULL;
}
$this->_csrf_set_hash();
$this->csrf_set_cookie();
if ($valid !== TRUE)
{
$this->csrf_show_error();
}
log_message('info', 'CSRF token verified');
return $this;
}
// --------------------------------------------------------------------
/**
* CSRF Set Cookie
*
* @codeCoverageIgnore
* @return CI_Security
*/
public function csrf_set_cookie()
{
$expire = time() + $this->_csrf_expire;
$secure_cookie = (bool) config_item('cookie_secure');
if ($secure_cookie && ! is_https())
{
return FALSE;
}
setcookie(
$this->_csrf_cookie_name,
$this->_csrf_hash,
$expire,
config_item('cookie_path'),
config_item('cookie_domain'),
$secure_cookie,
config_item('cookie_httponly')
);
log_message('info', 'CSRF cookie sent');
return $this;
}
// --------------------------------------------------------------------
/**
* Show CSRF Error
*
* @return void
*/
public function csrf_show_error()
{
show_error('The action you have requested is not allowed.', 403);
}
// --------------------------------------------------------------------
/**
* Get CSRF Hash
*
* @see CI_Security::$_csrf_hash
* @return string CSRF hash
*/
public function get_csrf_hash()
{
return $this->_csrf_hash;
}
// --------------------------------------------------------------------
/**
* Get CSRF Token Name
*
* @see CI_Security::$_csrf_token_name
* @return string CSRF token name
*/
public function get_csrf_token_name()
{
return $this->_csrf_token_name;
}
// --------------------------------------------------------------------
/**
* XSS Clean
*
* Sanitizes data so that Cross Site Scripting Hacks can be
* prevented. This method does a fair amount of work but
* it is extremely thorough, designed to prevent even the
* most obscure XSS attempts. Nothing is ever 100% foolproof,
* of course, but I haven't been able to get anything passed
* the filter.
*
* Note: Should only be used to deal with data upon submission.
* It's not something that should be used for general
* runtime processing.
*
* @link http://channel.bitflux.ch/wiki/XSS_Prevention
* Based in part on some code and ideas from Bitflux.
*
* @link http://ha.ckers.org/xss.html
* To help develop this script I used this great list of
* vulnerabilities along with a few other hacks I've
* harvested from examining vulnerabilities in other programs.
*
* @param string|string[] $str Input data
* @param bool $is_image Whether the input is an image
* @return string
*/
public function xss_clean($str, $is_image = FALSE)
{
// Is the string an array?
if (is_array($str))
{
foreach ($str as $key => &$value)
{
$str[$key] = $this->xss_clean($value);
}
return $str;
}
// Remove Invisible Characters
$str = remove_invisible_characters($str);
/*
* URL Decode
*
* Just in case stuff like this is submitted:
*
* <a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a>
*
* Note: Use rawurldecode() so it does not remove plus signs
*/
if (stripos($str, '%') !== false)
{
do
{
$oldstr = $str;
$str = rawurldecode($str);
$str = preg_replace_callback('#%(?:\s*[0-9a-f]){2,}#i', array($this, '_urldecodespaces'), $str);
}
while ($oldstr !== $str);
unset($oldstr);
}
/*
* Convert character entities to ASCII
*
* This permits our tests below to work reliably.
* We only convert entities that are within tags since
* these are the ones that will pose security problems.
*/
$str = preg_replace_callback("/[^a-z0-9>]+[a-z0-9]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str);
$str = preg_replace_callback('/<\w+.*/si', array($this, '_decode_entity'), $str);
// Remove Invisible Characters Again!
$str = remove_invisible_characters($str);
/*
* Convert all tabs to spaces
*
* This prevents strings like this: ja vascript
* NOTE: we deal with spaces between characters later.
* NOTE: preg_replace was found to be amazingly slow here on
* large blocks of data, so we use str_replace.
*/
$str = str_replace("\t", ' ', $str);
// Capture converted string for later comparison
$converted_string = $str;
// Remove Strings that are never allowed
$str = $this->_do_never_allowed($str);
/*
* Makes PHP tags safe
*
* Note: XML tags are inadvertently replaced too:
*
* <?xml
*
* But it doesn't seem to pose a problem.
*/
if ($is_image === TRUE)
{
// Images have a tendency to have the PHP short opening and
// closing tags every so often so we skip those and only
// do the long opening tags.
$str = preg_replace('/<\?(php)/i', '<?\\1', $str);
}
else
{
$str = str_replace(array('<?', '?'.'>'), array('<?', '?>'), $str);
}
/*
* Compact any exploded words
*
* This corrects words like: j a v a s c r i p t
* These words are compacted back to their correct state.
*/
$words = array(
'javascript', 'expression', 'vbscript', 'jscript', 'wscript',
'vbs', 'script', 'base64', 'applet', 'alert', 'document',
'write', 'cookie', 'window', 'confirm', 'prompt', 'eval'
);
foreach ($words as $word)
{
$word = implode('\s*', str_split($word)).'\s*';
// We only want to do this when it is followed by a non-word character
// That way valid stuff like "dealer to" does not become "dealerto"
$str = preg_replace_callback('#('.substr($word, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str);
}
/*
* Remove disallowed Javascript in links or img tags
* We used to do some version comparisons and use of stripos(),
* but it is dog slow compared to these simplified non-capturing
* preg_match(), especially if the pattern exists in the string
*
* Note: It was reported that not only space characters, but all in
* the following pattern can be parsed as separators between a tag name
* and its attributes: [\d\s"\'`;,\/\=\(\x00\x0B\x09\x0C]
* ... however, remove_invisible_characters() above already strips the
* hex-encoded ones, so we'll skip them below.
*/
do
{
$original = $str;
if (preg_match('/<a/i', $str))
{
$str = preg_replace_callback('#<a(?:rea)?[^a-z0-9>]+([^>]*?)(?:>|$)#si', array($this, '_js_link_removal'), $str);
}
if (preg_match('/<img/i', $str))
{
$str = preg_replace_callback('#<img[^a-z0-9]+([^>]*?)(?:\s?/?>|$)#si', array($this, '_js_img_removal'), $str);
}
if (preg_match('/script|xss/i', $str))
{
$str = preg_replace('#</*(?:script|xss).*?>#si', '[removed]', $str);
}
}
while ($original !== $str);
unset($original);
/*
* Sanitize naughty HTML elements
*
* If a tag containing any of the words in the list
* below is found, the tag gets converted to entities.
*
* So this: <blink>
* Becomes: <blink>
*/
$pattern = '#'
.'<((?<slash>/*\s*)((?<tagName>[a-z0-9]+)(?=[^a-z0-9]|$)|.+)' // tag start and name, followed by a non-tag character
.'[^\s\042\047a-z0-9>/=]*' // a valid attribute character immediately after the tag would count as a separator
// optional attributes
.'(?<attributes>(?:[\s\042\047/=]*' // non-attribute characters, excluding > (tag close) for obvious reasons
.'[^\s\042\047>/=]+' // attribute characters
// optional attribute-value
.'(?:\s*=' // attribute-value separator
.'(?:[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*))' // single, double or non-quoted value
.')?' // end optional attribute-value group
.')*)' // end optional attributes group
.'[^>]*)(?<closeTag>\>)?#isS';
// Note: It would be nice to optimize this for speed, BUT
// only matching the naughty elements here results in
// false positives and in turn - vulnerabilities!
do
{
$old_str = $str;
$str = preg_replace_callback($pattern, array($this, '_sanitize_naughty_html'), $str);
}
while ($old_str !== $str);
unset($old_str);
/*
* Sanitize naughty scripting elements
*
* Similar to above, only instead of looking for
* tags it looks for PHP and JavaScript commands
* that are disallowed. Rather than removing the
* code, it simply converts the parenthesis to entities
* rendering the code un-executable.
*
* For example: eval('some code')
* Becomes: eval('some code')
*/
$str = preg_replace(
'#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si',
'\\1\\2(\\3)',
$str
);
// Same thing, but for "tag functions" (e.g. eval`some code`)
// See https://github.com/bcit-ci/CodeIgniter/issues/5420
$str = preg_replace(
'#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)`(.*?)`#si',
'\\1\\2`\\3`',
$str
);
// Final clean up
// This adds a bit of extra precaution in case
// something got through the above filters
$str = $this->_do_never_allowed($str);
/*
* Images are Handled in a Special Way
* - Essentially, we want to know that after all of the character
* conversion is done whether any unwanted, likely XSS, code was found.
* If not, we return TRUE, as the image is clean.
* However, if the string post-conversion does not matched the
* string post-removal of XSS, then it fails, as there was unwanted XSS
* code found and removed/changed during processing.
*/
if ($is_image === TRUE)
{
return ($str === $converted_string);
}
return $str;
}
// --------------------------------------------------------------------
/**
* XSS Hash
*
* Generates the XSS hash if needed and returns it.
*
* @see CI_Security::$_xss_hash
* @return string XSS hash
*/
public function xss_hash()
{
if ($this->_xss_hash === NULL)
{
$rand = $this->get_random_bytes(16);
$this->_xss_hash = ($rand === FALSE)
? md5(uniqid(mt_rand(), TRUE))
: bin2hex($rand);
}
return $this->_xss_hash;
}
// --------------------------------------------------------------------
/**
* Get random bytes
*
* @param int $length Output length
* @return string
*/
public function get_random_bytes($length)
{
if (empty($length) OR ! ctype_digit((string) $length))
{
return FALSE;
}
if (function_exists('random_bytes'))
{
try
{
// The cast is required to avoid TypeError
return random_bytes((int) $length);
}
catch (Exception $e)
{
// If random_bytes() can't do the job, we can't either ...
// There's no point in using fallbacks.
log_message('error', $e->getMessage());
return FALSE;
}
}
// Unfortunately, none of the following PRNGs is guaranteed to exist ...
if (defined('MCRYPT_DEV_URANDOM') && ($output = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)) !== FALSE)
{
return $output;
}
if (is_readable('/dev/urandom') && ($fp = fopen('/dev/urandom', 'rb')) !== FALSE)
{
// Try not to waste entropy ...
is_php('5.4') && stream_set_chunk_size($fp, $length);
$output = fread($fp, $length);
fclose($fp);
if ($output !== FALSE)
{
return $output;
}
}
if (function_exists('openssl_random_pseudo_bytes'))
{
return openssl_random_pseudo_bytes($length);
}
return FALSE;
}
// --------------------------------------------------------------------
/**
* HTML Entities Decode
*
* A replacement for html_entity_decode()
*
* The reason we are not using html_entity_decode() by itself is because
* while it is not technically correct to leave out the semicolon
* at the end of an entity most browsers will still interpret the entity
* correctly. html_entity_decode() does not convert entities without
* semicolons, so we are left with our own little solution here. Bummer.
*
* @link http://php.net/html-entity-decode
*
* @param string $str Input
* @param string $charset Character set
* @return string
*/
public function entity_decode($str, $charset = NULL)
{
if (strpos($str, '&') === FALSE)
{
return $str;
}
static $_entities;
isset($charset) OR $charset = $this->charset;
$flag = is_php('5.4')
? ENT_COMPAT | ENT_HTML5
: ENT_COMPAT;
if ( ! isset($_entities))
{
$_entities = array_map('strtolower', get_html_translation_table(HTML_ENTITIES, $flag, $charset));
// If we're not on PHP 5.4+, add the possibly dangerous HTML 5
// entities to the array manually
if ($flag === ENT_COMPAT)
{
$_entities[':'] = ':';
$_entities['('] = '(';
$_entities[')'] = ')';
$_entities["\n"] = '
';
$_entities["\t"] = '	';
}
}
do
{
$str_compare = $str;
// Decode standard entities, avoiding false positives
if (preg_match_all('/&[a-z]{2,}(?![a-z;])/i', $str, $matches))
{
$replace = array();
$matches = array_unique(array_map('strtolower', $matches[0]));
foreach ($matches as &$match)
{
if (($char = array_search($match.';', $_entities, TRUE)) !== FALSE)
{
$replace[$match] = $char;
}
}
$str = str_replace(array_keys($replace), array_values($replace), $str);
}
// Decode numeric & UTF16 two byte entities
$str = html_entity_decode(
preg_replace('/(&#(?:x0*[0-9a-f]{2,5}(?![0-9a-f;])|(?:0*\d{2,4}(?![0-9;]))))/iS', '$1;', $str),
$flag,
$charset
);
if ($flag === ENT_COMPAT)
{
$str = str_replace(array_values($_entities), array_keys($_entities), $str);
}
}
while ($str_compare !== $str);
return $str;
}
// --------------------------------------------------------------------
/**
* Sanitize Filename
*
* @param string $str Input file name
* @param bool $relative_path Whether to preserve paths
* @return string
*/
public function sanitize_filename($str, $relative_path = FALSE)
{
$bad = $this->filename_bad_chars;
if ( ! $relative_path)
{
$bad[] = './';
$bad[] = '/';
}
$str = remove_invisible_characters($str, FALSE);
do
{
$old = $str;
$str = str_replace($bad, '', $str);
}
while ($old !== $str);
return stripslashes($str);
}
// ----------------------------------------------------------------
/**
* Strip Image Tags
*
* @param string $str
* @return string
*/
public function strip_image_tags($str)
{
return preg_replace(
array(
'#<img[\s/]+.*?src\s*=\s*(["\'])([^\\1]+?)\\1.*?\>#i',
'#<img[\s/]+.*?src\s*=\s*?(([^\s"\'=<>`]+)).*?\>#i'
),
'\\2',
$str
);
}
// ----------------------------------------------------------------
/**
* URL-decode taking spaces into account
*
* @see https://github.com/bcit-ci/CodeIgniter/issues/4877
* @param array $matches
* @return string
*/
protected function _urldecodespaces($matches)
{
$input = $matches[0];
$nospaces = preg_replace('#\s+#', '', $input);
return ($nospaces === $input)
? $input
: rawurldecode($nospaces);
}
// ----------------------------------------------------------------
/**
* Compact Exploded Words
*
* Callback method for xss_clean() to remove whitespace from
* things like 'j a v a s c r i p t'.
*
* @used-by CI_Security::xss_clean()
* @param array $matches
* @return string
*/
protected function _compact_exploded_words($matches)
{
return preg_replace('/\s+/s', '', $matches[1]).$matches[2];
}
// --------------------------------------------------------------------
/**
* Sanitize Naughty HTML
*
* Callback method for xss_clean() to remove naughty HTML elements.
*
* @used-by CI_Security::xss_clean()
* @param array $matches
* @return string
*/
protected function _sanitize_naughty_html($matches)
{
static $naughty_tags = array(
'alert', 'area', 'prompt', 'confirm', 'applet', 'audio', 'basefont', 'base', 'behavior', 'bgsound',
'blink', 'body', 'embed', 'expression', 'form', 'frameset', 'frame', 'head', 'html', 'ilayer',
'iframe', 'input', 'button', 'select', 'isindex', 'layer', 'link', 'meta', 'keygen', 'object',
'plaintext', 'style', 'script', 'textarea', 'title', 'math', 'video', 'svg', 'xml', 'xss'
);
static $evil_attributes = array(
'on\w+', 'style', 'xmlns', 'formaction', 'form', 'xlink:href', 'FSCommand', 'seekSegmentTime'
);
// First, escape unclosed tags
if (empty($matches['closeTag']))
{
return '<'.$matches[1];
}
// Is the element that we caught naughty? If so, escape it
elseif (in_array(strtolower($matches['tagName']), $naughty_tags, TRUE))
{
return '<'.$matches[1].'>';
}
// For other tags, see if their attributes are "evil" and strip those
elseif (isset($matches['attributes']))
{
// We'll store the already filtered attributes here
$attributes = array();
// Attribute-catching pattern
$attributes_pattern = '#'
.'(?<name>[^\s\042\047>/=]+)' // attribute characters
// optional attribute-value
.'(?:\s*=(?<value>[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*)))' // attribute-value separator
.'#i';
// Blacklist pattern for evil attribute names
$is_evil_pattern = '#^('.implode('|', $evil_attributes).')$#i';
// Each iteration filters a single attribute
do
{
// Strip any non-alpha characters that may precede an attribute.
// Browsers often parse these incorrectly and that has been a
// of numerous XSS issues we've had.
$matches['attributes'] = preg_replace('#^[^a-z]+#i', '', $matches['attributes']);
if ( ! preg_match($attributes_pattern, $matches['attributes'], $attribute, PREG_OFFSET_CAPTURE))
{
// No (valid) attribute found? Discard everything else inside the tag
break;
}
if (
// Is it indeed an "evil" attribute?
preg_match($is_evil_pattern, $attribute['name'][0])
// Or does it have an equals sign, but no value and not quoted? Strip that too!
OR (trim($attribute['value'][0]) === '')
)
{
$attributes[] = 'xss=removed';
}
else
{
$attributes[] = $attribute[0][0];
}
$matches['attributes'] = substr($matches['attributes'], $attribute[0][1] + strlen($attribute[0][0]));
}
while ($matches['attributes'] !== '');
$attributes = empty($attributes)
? ''
: ' '.implode(' ', $attributes);
return '<'.$matches['slash'].$matches['tagName'].$attributes.'>';
}
return $matches[0];
}
// --------------------------------------------------------------------
/**
* JS Link Removal
*
* Callback method for xss_clean() to sanitize links.
*
* This limits the PCRE backtracks, making it more performance friendly
* and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
* PHP 5.2+ on link-heavy strings.
*
* @used-by CI_Security::xss_clean()
* @param array $match
* @return string
*/
protected function _js_link_removal($match)
{
return str_replace(
$match[1],
preg_replace(
'#href=.*?(?:(?:alert|prompt|confirm)(?:\(|&\#40;|`|&\#96;)|javascript:|livescript:|mocha:|charset=|window\.|\(?document\)?\.|\.cookie|<script|<xss|d\s*a\s*t\s*a\s*:)#si',
'',
$this->_filter_attributes($match[1])
),
$match[0]
);
}
// --------------------------------------------------------------------
/**
* JS Image Removal
*
* Callback method for xss_clean() to sanitize image tags.
*
* This limits the PCRE backtracks, making it more performance friendly
* and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
* PHP 5.2+ on image tag heavy strings.
*
* @used-by CI_Security::xss_clean()
* @param array $match
* @return string
*/
protected function _js_img_removal($match)
{
return str_replace(
$match[1],
preg_replace(
'#src=.*?(?:(?:alert|prompt|confirm|eval)(?:\(|&\#40;|`|&\#96;)|javascript:|livescript:|mocha:|charset=|window\.|\(?document\)?\.|\.cookie|<script|<xss|base64\s*,)#si',
'',
$this->_filter_attributes($match[1])
),
$match[0]
);
}
// --------------------------------------------------------------------
/**
* Attribute Conversion
*
* @used-by CI_Security::xss_clean()
* @param array $match
* @return string
*/
protected function _convert_attribute($match)
{
return str_replace(array('>', '<', '\\'), array('>', '<', '\\\\'), $match[0]);
}
// --------------------------------------------------------------------
/**
* Filter Attributes
*
* Filters tag attributes for consistency and safety.
*
* @used-by CI_Security::_js_img_removal()
* @used-by CI_Security::_js_link_removal()
* @param string $str
* @return string
*/
protected function _filter_attributes($str)
{
$out = '';
if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches))
{
foreach ($matches[0] as $match)
{
$out .= preg_replace('#/\*.*?\*/#s', '', $match);
}
}
return $out;
}
// --------------------------------------------------------------------
/**
* HTML Entity Decode Callback
*
* @used-by CI_Security::xss_clean()
* @param array $match
* @return string
*/
protected function _decode_entity($match)
{
// Protect GET variables in URLs
// 901119URL5918AMP18930PROTECT8198
$match = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-/]+)|i', $this->xss_hash().'\\1=\\2', $match[0]);
// Decode, then un-protect URL GET vars
return str_replace(
$this->xss_hash(),
'&',
$this->entity_decode($match, $this->charset)
);
}
// --------------------------------------------------------------------
/**
* Do Never Allowed
*
* @used-by CI_Security::xss_clean()
* @param string
* @return string
*/
protected function _do_never_allowed($str)
{
$str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str);
foreach ($this->_never_allowed_regex as $regex)
{
$str = preg_replace('#'.$regex.'#is', '[removed]', $str);
}
return $str;
}
// --------------------------------------------------------------------
/**
* Set CSRF Hash and Cookie
*
* @return string
*/
protected function _csrf_set_hash()
{
if ($this->_csrf_hash === NULL)
{
// If the cookie exists we will use its value.
// We don't necessarily want to regenerate it with
// each page load since a page could contain embedded
// sub-pages causing this feature to fail
if (isset($_COOKIE[$this->_csrf_cookie_name]) && is_string($_COOKIE[$this->_csrf_cookie_name])
&& preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name]) === 1)
{
return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name];
}
$rand = $this->get_random_bytes(16);
$this->_csrf_hash = ($rand === FALSE)
? md5(uniqid(mt_rand(), TRUE))
: bin2hex($rand);
}
return $this->_csrf_hash;
}
}