DB.php
最后更新于:2022-04-01 20:46:26
~~~
$dns['scheme'],
'hostname' => (isset($dns['host'])) ? rawurldecode($dns['host']) : '',
'username' => (isset($dns['user'])) ? rawurldecode($dns['user']) : '',
'password' => (isset($dns['pass'])) ? rawurldecode($dns['pass']) : '',
'database' => (isset($dns['path'])) ? rawurldecode(substr($dns['path'], 1)) : ''
);
// were additional config items set?
// 是否设置了额外的配置项
if (isset($dns['query']))
{
// parse_str()解析查询字符串到变量中
parse_str($dns['query'], $extra);
foreach ($extra as $key => $val)
{
// booleans please
// 把字符串转换true,false转换为真正boolean值
if (strtoupper($val) == "TRUE")
{
$val = TRUE;
}
elseif (strtoupper($val) == "FALSE")
{
$val = FALSE;
}
$params[$key] = $val;
}
}
}
// No DB specified yet? Beat them senseless...
// 没有指定要连接的数据库驱动
if ( ! isset($params['dbdriver']) OR $params['dbdriver'] == '')
{
show_error('You have not selected a database type to connect to.');
}
// Load the DB classes. Note: Since the active record class is optional
// we need to dynamically create a class that extends proper parent class
// based on whether we're using the active record class or not.
// Kudos to Paul for discovering this clever use of eval()
// 载入DB类。 注意: 因为active record类是可选的,我们需要动态的去
// 创建一个继承适当的父类(基于我们是否使用active record类)
if ($active_record_override !== NULL)
{
$active_record = $active_record_override;
}
require_once(BASEPATH.'database/DB_driver.php');
// 启用active_record
if ( ! isset($active_record) OR $active_record == TRUE)
{
require_once(BASEPATH.'database/DB_active_rec.php');
// 动态创建CI_DB类继承自CI_DB_active_record类
if ( ! class_exists('CI_DB'))
{
eval('class CI_DB extends CI_DB_active_record { }');
}
}
else
{
// 动态创建CI_DB直接继承自CI_DB_driver
if ( ! class_exists('CI_DB'))
{
eval('class CI_DB extends CI_DB_driver { }');
}
}
// 加载响应的数据库驱动
require_once(BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php');
// Instantiate the DB adapter
// 实例化DB适配器
$driver = 'CI_DB_'.$params['dbdriver'].'_driver';
$DB = new $driver($params);
// 在DB_driver中的字段,用于指定是否要对数据库驱动进行初始化操作
if ($DB->autoinit == TRUE)
{
$DB->initialize();
}
if (isset($params['stricton']) && $params['stricton'] == TRUE)
{
$DB->query('SET SESSION sql_mode="STRICT_ALL_TABLES"');
}
return $DB;
}
/* End of file DB.php */
/* Location: ./system/database/DB.php */
~~~
';
Exceptions
最后更新于:2022-04-01 20:46:24
~~~
'Error',
E_WARNING => 'Warning',
E_PARSE => 'Parsing Error',
E_NOTICE => 'Notice',
E_CORE_ERROR => 'Core Error',
E_CORE_WARNING => 'Core Warning',
E_COMPILE_ERROR => 'Compile Error',
E_COMPILE_WARNING => 'Compile Warning',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Runtime Notice'
);
/**
* Constructor
*/
public function __construct()
{
$this->ob_level = ob_get_level();
// Note: Do not log messages from this constructor.
}
// --------------------------------------------------------------------
/**
* Exception Logger
* 异常日志的记录
* This function logs PHP generated error messages
* 这个函数将记录php产生的错误消息
* @access private
* @param string the error severity
* @param string the error string
* @param string the error filepath
* @param string the error line number
* @return string
*/
function log_exception($severity, $message, $filepath, $line)
{
// 根据错误级别设置错误严重性
$severity = ( ! isset($this->levels[$severity])) ? $severity : $this->levels[$severity];
log_message('error', 'Severity: '.$severity.' --> '.$message. ' '.$filepath.' '.$line, TRUE);
}
// --------------------------------------------------------------------
/**
* 404 Page Not Found Handler
* 404 页面
* @access private
* @param string the page
* @param bool log error yes/no
* @return string
*/
function show_404($page = '', $log_error = TRUE)
{
$heading = "404 Page Not Found";
$message = "The page you requested was not found.";
// By default we log this, but allow a dev to skip it
if ($log_error)
{
log_message('error', '404 Page Not Found --> '.$page);
}
echo $this->show_error($heading, $message, 'error_404', 404);
exit;
}
// --------------------------------------------------------------------
/**
* General Error Page
* 一般的错误页面
* This function takes an error message as input
* (either as a string or an array) and displays
* it using the specified template.
* 这个函数将使用指定的模板显示一个错误消息
* @access private
* @param string the heading
* @param string the message
* @param string the template name
* @param int the status code
* @return string
*/
function show_error($heading, $message, $template = 'error_general', $status_code = 500)
{
// 默认是500
set_status_header($status_code);
// 组合错误消息
$message = '
';
'.implode('
', ( ! is_array($message)) ? array($message) : $message).'
'; /* * 如果还没看过core/Loader.php,下面这个判断可能让人有点迷惑。 * ob_get_level()是取得当前缓冲机制的嵌套级别。(缓冲是可以一层嵌一层的。) * 右边的$this->ob_level是在__construct()里面同样通过ob_get_level()被赋值的。 * 也就是说,有可能出现:Exception组件被加载时(也就是应用刚开始运行时) * 的缓冲级别(其实也就是程序最开始的时候的缓冲级别,那时 * 候是还没有ob_start()过的),与发生错误的时候的缓冲级别相差1。 * 在控制器执行$this->load->view("xxx");的时候,实质, * Loader引入并执行这个视图文件的时候,是先把缓冲打开,即 * 先ob_start(),所有输出放到缓冲区(详见:core/Loader.php中的_ci_load()),然后再由Output处理输出。 * 因此,如果是在视图文件发生错误,则就会出现缓冲级别相差1的情况 * 这就会导致输出的内容不仅仅只是这个的错误信息 * 此时先把输出的内容给flush出来,然后再把错误信息输出。 * */ if (ob_get_level() > $this->ob_level + 1) { ob_end_flush(); } ob_start(); include(APPPATH.'errors/'.$template.'.php'); $buffer = ob_get_contents(); ob_end_clean(); return $buffer; } // -------------------------------------------------------------------- /** * Native PHP error handler * 本地php错误处理 * @access private * @param string the error severity * @param string the error string * @param string the error filepath * @param string the error line number * @return string */ function show_php_error($severity, $message, $filepath, $line) { $severity = ( ! isset($this->levels[$severity])) ? $severity : $this->levels[$severity]; $filepath = str_replace("\\", "/", $filepath); // For safety reasons we do not show the full file path if (FALSE !== strpos($filepath, '/')) { $x = explode('/', $filepath); $filepath = $x[count($x)-2].'/'.end($x); } if (ob_get_level() > $this->ob_level + 1) { ob_end_flush(); } ob_start(); include(APPPATH.'errors/error_php.php'); $buffer = ob_get_contents(); ob_end_clean(); echo $buffer; } } // END Exceptions Class /* End of file Exceptions.php */ /* Location: ./system/core/Exceptions.php */ ~~~Model.php
最后更新于:2022-04-01 20:46:22
~~~
$key;
}
}
// END Model Class
/* End of file Model.php */
/* Location: ./system/core/Model.php */
~~~
';
Controller.php
最后更新于:2022-04-01 20:46:19
~~~
$class)
{
$this->$var =& load_class($class);
}
// 给超级控制器加载Loader组件,这个组件是它的好助手,
// 很多时候你会经常用到$this->load->xxx()的形式加载某个东西,
// 这个load就是控制器被构造的时候就伴随存在的。
$this->load =& load_class('Loader', 'core');
// 初始化Loader组件,详细Loader.php
$this->load->initialize();
log_message('debug', "Controller Class Initialized");
}
public static function &get_instance()
{
return self::$instance;
}
}
// END Controller class
/* End of file Controller.php */
/* Location: ./system/core/Controller.php */
~~~
';
Lang.php
最后更新于:2022-04-01 20:46:17
~~~
is_loaded和$this->language中去
* @param bool add suffix to $langfile 文件是否添加后缀
* @param string alternative path to look for language file 语言包文件的自定义路径
* @return mixed
*/
function load($langfile = '', $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '')
{
// langfile 文件的.php 后缀去掉
$langfile = str_replace('.php', '', $langfile);
// 判断需不需要添加后缀如果需要
// 将_lang. 去掉并再langfile后面添加_lang
if ($add_suffix == TRUE)
{
$langfile = str_replace('_lang.', '', $langfile).'_lang';
}
// 为langfile添加.php后缀
$langfile .= '.php';
// 判断当前文件是否被加载过
if (in_array($langfile, $this->is_loaded, TRUE))
{
return;
}
// 获取配置文件的数据
$config =& get_config();
// 如果要使用的语言为空
// 那么 我们将从$config中获取
if ($idiom == '')
{
$deft_lang = ( ! isset($config['language'])) ? 'english' : $config['language'];
$idiom = ($deft_lang == '') ? 'english' : $deft_lang;
}
// Determine where the language file is and load it
// 在自定义路径下寻找语言包并加载
if ($alt_path != '' && file_exists($alt_path.'language/'.$idiom.'/'.$langfile))
{
include($alt_path.'language/'.$idiom.'/'.$langfile);
}
else
{
// 如果自定义路径下没找到调用get_instance()->load->get_package_paths(TRUE)
// 在包路径下寻找
// get_package_paths这个函数在loader.php中
$found = FALSE;
foreach (get_instance()->load->get_package_paths(TRUE) as $package_path)
{
if (file_exists($package_path.'language/'.$idiom.'/'.$langfile))
{
include($package_path.'language/'.$idiom.'/'.$langfile);
$found = TRUE;
break;
}
}
// 如果还没找到就只能报错了
//
if ($found !== TRUE)
{
show_error('Unable to load the requested language file: language/'.$idiom.'/'.$langfile);
}
}
if ( ! isset($lang))
{
log_message('error', 'Language file contains no data: language/'.$idiom.'/'.$langfile);
return;
}
if ($return == TRUE)
{
return $lang;
}
$this->is_loaded[] = $langfile;
$this->language = array_merge($this->language, $lang);
unset($lang);
log_message('debug', 'Language file loaded: language/'.$idiom.'/'.$langfile);
return TRUE;
}
// --------------------------------------------------------------------
/**
* Fetch a single line of text from the language array
* 获取一行文本
* @access public
* @param string $line the language line
* @return string
*/
function line($line = '')
{
/*
* $this->language 的样子
* $lang['error_email_missing'] = "You must submit an email address";
* $lang['error_url_missing'] = "You must submit a URL";
* $lang['error_username_missing'] = "You must submit a username";
*/
$value = ($line == '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line];
// Because killer robots like unicorns!
if ($value === FALSE)
{
log_message('error', 'Could not find the language line "'.$line.'"');
}
return $value;
}
}
// END Language Class
/* End of file Lang.php */
/* Location: ./system/core/Lang.php */
~~~
';
Input.php
最后更新于:2022-04-01 20:46:15
~~~
_allow_get_array = (config_item('allow_get_array') === TRUE);
$this->_enable_xss = (config_item('global_xss_filtering') === TRUE);
$this->_enable_csrf = (config_item('csrf_protection') === TRUE);
// 清除globals变量,在开启了globals_register的情况下,相当于关闭了此配置。
// 开启一道 安全防护
global $SEC;
$this->security =& $SEC;
// Do we need the UTF-8 class?
if (UTF8_ENABLED === TRUE)
{
global $UNI;
$this->uni =& $UNI;
}
// Sanitize global arrays
$this->_sanitize_globals();
}
// --------------------------------------------------------------------
/**
* Fetch from array
* 从$array获取值,如果设置了xss_clean 那么进行过滤
* This is a helper function to retrieve 检索 values from global arrays
* 这是一个帮助函数用来从全局数组中检索
*
* @access private
* @param array
* @param string
* @param bool
* @return string
*/
function _fetch_from_array(&$array, $index = '', $xss_clean = FALSE)
{
if ( ! isset($array[$index]))
{
return FALSE;
}
if ($xss_clean === TRUE)
{
return $this->security->xss_clean($array[$index]);
}
return $array[$index];
}
// --------------------------------------------------------------------
/**
* Fetch an item from the GET array
* 获取过滤后的GET数组
* @access public
* @param string
* @param bool
* @return string
*/
function get($index = NULL, $xss_clean = FALSE)
{
// Check if a field has been provided
// 检查是否一个字段已经被提供
if ($index === NULL AND ! empty($_GET))
{
$get = array();
// loop through the full _GET array
// 遍历_GET数组
foreach (array_keys($_GET) as $key)
{
$get[$key] = $this->_fetch_from_array($_GET, $key, $xss_clean);
}
return $get;
}
return $this->_fetch_from_array($_GET, $index, $xss_clean);
}
// --------------------------------------------------------------------
/**
* Fetch an item from the POST array
* 获取过滤后的$_POST值
* @access public
* @param string
* @param bool
* @return string
*/
function post($index = NULL, $xss_clean = FALSE)
{
// Check if a field has been provided
if ($index === NULL AND ! empty($_POST))
{
$post = array();
// Loop through the full _POST array and return it
foreach (array_keys($_POST) as $key)
{
$post[$key] = $this->_fetch_from_array($_POST, $key, $xss_clean);
}
return $post;
}
return $this->_fetch_from_array($_POST, $index, $xss_clean);
}
// --------------------------------------------------------------------
/**
* Fetch an item from either the GET array or the POST
* 从get和post中获取值, post优先
* @access public
* @param string The index key
* @param bool XSS cleaning
* @return string
*/
function get_post($index = '', $xss_clean = FALSE)
{
if ( ! isset($_POST[$index]) )
{
return $this->get($index, $xss_clean);
}
else
{
return $this->post($index, $xss_clean);
}
}
// --------------------------------------------------------------------
/**
* Fetch an item from the COOKIE array
* 返回过滤后的COOKIE值
* @access public
* @param string
* @param bool
* @return string
*/
function cookie($index = '', $xss_clean = FALSE)
{
return $this->_fetch_from_array($_COOKIE, $index, $xss_clean);
}
// ------------------------------------------------------------------------
/**
* Set cookie
*
* Accepts six parameter, or you can submit an associative
* array in the first parameter containing all the values.
* 接收6个参数或者接收一个关联数组里面包含所有的值
* @access public
* @param mixed
* @param string the value of the cookie
* @param string the number of seconds until expiration
* @param string the cookie domain. Usually: .yourdomain.com
* @param string the cookie path
* @param string the cookie prefix
* @param bool true makes the cookie secure
* @return void
*/
function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE)
{
// 如果第一个值是数组 将数组中的值分别赋值给留个参数
if (is_array($name))
{
// always leave 'name' in last place, as the loop will break otherwise, due to $$item
foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'name') as $item)
{
if (isset($name[$item]))
{
$$item = $name[$item];
}
}
}
// 如果某个参数为默认值但是config.php中的配置不是默认值
// 则使用config.php中的配置值
if ($prefix == '' AND config_item('cookie_prefix') != '')
{
$prefix = config_item('cookie_prefix');
}
if ($domain == '' AND config_item('cookie_domain') != '')
{
$domain = config_item('cookie_domain');
}
if ($path == '/' AND config_item('cookie_path') != '/')
{
$path = config_item('cookie_path');
}
if ($secure == FALSE AND config_item('cookie_secure') != FALSE)
{
$secure = config_item('cookie_secure');
}
if ( ! is_numeric($expire))
{
$expire = time() - 86500;
}
else
{
$expire = ($expire > 0) ? time() + $expire : 0;
}
setcookie($prefix.$name, $value, $expire, $path, $domain, $secure);
}
// --------------------------------------------------------------------
/**
* Fetch an item from the SERVER array
* 返回过滤后的$_SERVER值
* @access public
* @param string
* @param bool
* @return string
*/
function server($index = '', $xss_clean = FALSE)
{
return $this->_fetch_from_array($_SERVER, $index, $xss_clean);
}
// --------------------------------------------------------------------
/**
* Fetch the IP Address
* 返回当前用户的IP。如果IP地址无效,返回0.0.0.0的IP:
* @return string
*/
public function ip_address()
{
// 如果已经有了ip_address 则返回
if ($this->ip_address !== FALSE)
{
return $this->ip_address;
}
$proxy_ips = config_item('proxy_ips');
if ( ! empty($proxy_ips))
{
$proxy_ips = explode(',', str_replace(' ', '', $proxy_ips));
foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header)
{
if (($spoof = $this->server($header)) !== FALSE)
{
// Some proxies typically list the whole chain of IP
// addresses through which the client has reached us.
// e.g. client_ip, proxy_ip1, proxy_ip2, etc.
if (strpos($spoof, ',') !== FALSE)
{
$spoof = explode(',', $spoof, 2);
$spoof = $spoof[0];
}
if ( ! $this->valid_ip($spoof))
{
$spoof = FALSE;
}
else
{
break;
}
}
}
$this->ip_address = ($spoof !== FALSE && in_array($_SERVER['REMOTE_ADDR'], $proxy_ips, TRUE))
? $spoof : $_SERVER['REMOTE_ADDR'];
}
else
{
$this->ip_address = $_SERVER['REMOTE_ADDR'];
}
if ( ! $this->valid_ip($this->ip_address))
{
$this->ip_address = '0.0.0.0';
}
return $this->ip_address;
}
// --------------------------------------------------------------------
/**
* Validate IP Address
* 测试输入的IP地址是不是有效,返回布尔值TRUE或者FALSE。
* 注意:$this->input->ip_address()自动测试输入的IP地址本身格式是不是有效。
* @access public
* @param string
* @param string ipv4 or ipv6
* @return bool
*/
public function valid_ip($ip, $which = '')
{
$which = strtolower($which);
// First check if filter_var is available
if (is_callable('filter_var'))
{
switch ($which) {
case 'ipv4':
$flag = FILTER_FLAG_IPV4;
break;
case 'ipv6':
$flag = FILTER_FLAG_IPV6;
break;
default:
$flag = '';
break;
}
return (bool) filter_var($ip, FILTER_VALIDATE_IP, $flag);
}
if ($which !== 'ipv6' && $which !== 'ipv4')
{
if (strpos($ip, ':') !== FALSE)
{
$which = 'ipv6';
}
elseif (strpos($ip, '.') !== FALSE)
{
$which = 'ipv4';
}
else
{
return FALSE;
}
}
$func = '_valid_'.$which;
return $this->$func($ip);
}
// --------------------------------------------------------------------
/**
* Validate IPv4 Address
* 验证ipv4地址
* Updated version suggested by Geert De Deckere
*
* @access protected
* @param string
* @return bool
*/
protected function _valid_ipv4($ip)
{
$ip_segments = explode('.', $ip);
// Always 4 segments needed
if (count($ip_segments) !== 4)
{
return FALSE;
}
// IP can not start with 0
if ($ip_segments[0][0] == '0')
{
return FALSE;
}
// Check each segment
foreach ($ip_segments as $segment)
{
// IP segments must be digits and can not be
// longer than 3 digits or greater then 255
if ($segment == '' OR preg_match("/[^0-9]/", $segment) OR $segment > 255 OR strlen($segment) > 3)
{
return FALSE;
}
}
return TRUE;
}
// --------------------------------------------------------------------
/**
* Validate IPv6 Address
* 验证ipv6地址
* @access protected
* @param string
* @return bool
*/
protected function _valid_ipv6($str)
{
// 8 groups, separated by :
// 0-ffff per group
// one set of consecutive 0 groups can be collapsed to ::
$groups = 8;
$collapsed = FALSE;
$chunks = array_filter(
preg_split('/(:{1,2})/', $str, NULL, PREG_SPLIT_DELIM_CAPTURE)
);
// Rule out easy nonsense
if (current($chunks) == ':' OR end($chunks) == ':')
{
return FALSE;
}
// PHP supports IPv4-mapped IPv6 addresses, so we'll expect those as well
if (strpos(end($chunks), '.') !== FALSE)
{
$ipv4 = array_pop($chunks);
if ( ! $this->_valid_ipv4($ipv4))
{
return FALSE;
}
$groups--;
}
while ($seg = array_pop($chunks))
{
if ($seg[0] == ':')
{
if (--$groups == 0)
{
return FALSE; // too many groups
}
if (strlen($seg) > 2)
{
return FALSE; // long separator
}
if ($seg == '::')
{
if ($collapsed)
{
return FALSE; // multiple collapsed
}
$collapsed = TRUE;
}
}
elseif (preg_match("/[^0-9a-f]/i", $seg) OR strlen($seg) > 4)
{
return FALSE; // invalid segment
}
}
return $collapsed OR $groups == 1;
}
// --------------------------------------------------------------------
/**
* User Agent
* 返回当前用户正在使用的浏览器的user agent信息。 如果不能得到数据,返回FALSE。
* 一般user_agent为空的时候被认定为手机访问,或者curl的抓取,或则会蜘蛛抓取
* @access public
* @return string
*/
function user_agent()
{
if ($this->user_agent !== FALSE)
{
return $this->user_agent;
}
$this->user_agent = ( ! isset($_SERVER['HTTP_USER_AGENT'])) ? FALSE : $_SERVER['HTTP_USER_AGENT'];
return $this->user_agent;
}
// --------------------------------------------------------------------
/**
* Sanitize Globals
* 清理全局数组
* This function does the following:
* 这个函数做了下面的操作:
* Unsets $_GET data (if query strings are not enabled)
* 销毁$_GET (如果query strings 没有开启)
* Unsets all globals if register_globals is enabled
* 销毁所有全局数组如果register_globals开启
*
* Standardizes newline characters to \n
* 标准化换行符\n
* @access private
* @return void
*/
function _sanitize_globals()
{
// It would be "wrong" to unset any of these GLOBALS.
// 销毁下面的全局数组将是错误的。
$protected = array('_SERVER', '_GET', '_POST', '_FILES', '_REQUEST',
'_SESSION', '_ENV', 'GLOBALS', 'HTTP_RAW_POST_DATA',
'system_folder', 'application_folder', 'BM', 'EXT',
'CFG', 'URI', 'RTR', 'OUT', 'IN');
// Unset globals for securiy.为了安全销毁除了上面之外的全局数组
// This is effectively the same as register_globals = off
// 这样的效果和register_globals是相同的
// 经过下面处理后,所有的非保护的全局变量将被删除掉
foreach (array($_GET, $_POST, $_COOKIE) as $global)
{
if ( ! is_array($global))
{
if ( ! in_array($global, $protected))
{
global $$global;
$$global = NULL;
}
}
else
{
foreach ($global as $key => $val)
{
if ( ! in_array($key, $protected))
{
global $$key;
$$key = NULL;
}
}
}
}
// Is $_GET data allowed? If not we'll set the $_GET to an empty array
// 是否允许$_GET数据? 如果不允许的话,设置$_GET为空数组
if ($this->_allow_get_array == FALSE)
{
$_GET = array();
}
else
{
if (is_array($_GET) AND count($_GET) > 0)
{
foreach ($_GET as $key => $val)
{
$_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
}
}
}
// Clean $_POST Data
// 过滤$_POST 数组
if (is_array($_POST) AND count($_POST) > 0)
{
foreach ($_POST as $key => $val)
{
$_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
}
}
// Clean $_COOKIE Data
// 过滤$_COOKIE数组
if (is_array($_COOKIE) AND count($_COOKIE) > 0)
{
// Also get rid of specially treated cookies that might be set by a server
// or silly application, that are of no use to a CI application anyway
// but that when present will trip our 'Disallowed Key Characters' alarm
// http://www.ietf.org/rfc/rfc2109.txt
// note that the key names below are single quoted strings, and are not PHP variables
unset($_COOKIE['$Version']);
unset($_COOKIE['$Path']);
unset($_COOKIE['$Domain']);
foreach ($_COOKIE as $key => $val)
{
$_COOKIE[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
}
}
// Sanitize PHP_SELF
$_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']);
// CSRF Protection check on HTTP requests
// CSRF保护检测http请求
if ($this->_enable_csrf == TRUE && ! $this->is_cli_request())
{
$this->security->csrf_verify();
}
log_message('debug', "Global POST and COOKIE data sanitized");
}
// --------------------------------------------------------------------
/**
* Clean Input Data
* 过滤input数据
* This is a helper function. It escapes data and
* standardizes newline characters to \n
*
* @access private
* @param string
* @return string
*/
function _clean_input_data($str)
{
if (is_array($str))
{
$new_array = array();
foreach ($str as $key => $val)
{
$new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
}
return $new_array;
}
/* We strip slashes if magic quotes is on to keep things consistent
如果小于PHP5.4版本,并且get_magic_quotes_gpc开启了,则去掉斜线。
NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and
it will probably not exist in future versions at all。
注意:在PHP5.4及之后版本,get_magic_quotes_gpc()将总是返回0,
在后续版本中可能会移除该特性
*/
if ( ! is_php('5.4') && get_magic_quotes_gpc())
{
$str = stripslashes($str);
}
// Clean UTF-8 if supported 如果支持清理utf8
if (UTF8_ENABLED === TRUE)
{
$str = $this->uni->clean_string($str);
}
// Remove control characters
$str = remove_invisible_characters($str);
// Should we filter the input data?
if ($this->_enable_xss === TRUE)
{
$str = $this->security->xss_clean($str);
}
// Standardize newlines if needed
if ($this->_standardize_newlines == TRUE)
{
if (strpos($str, "\r") !== FALSE)
{
$str = str_replace(array("\r\n", "\r", "\r\n\n"), PHP_EOL, $str);
}
}
return $str;
}
// --------------------------------------------------------------------
/**
* Clean Keys
* 过滤键值
* This is a helper function. To prevent malicious users
* from trying to exploit keys we make sure that keys are
* only named with alpha-numeric text and a few other items.
*
* @access private
* @param string
* @return string
*/
function _clean_input_keys($str)
{
if ( ! preg_match("/^[a-z0-9:_\/-]+$/i", $str))
{
exit('Disallowed Key Characters.');
}
// Clean UTF-8 if supported
if (UTF8_ENABLED === TRUE)
{
$str = $this->uni->clean_string($str);
}
return $str;
}
// --------------------------------------------------------------------
/**
* Request Headers
* 返回请求头(header)数组。
* In Apache, you can simply call apache_request_headers(), however for
* people running other webservers the function is undefined.
*
* @param bool XSS cleaning
*
* @return array
*/
public function request_headers($xss_clean = FALSE)
{
// Look at Apache go!
if (function_exists('apache_request_headers'))
{
$headers = apache_request_headers();
}
else
{
$headers['Content-Type'] = (isset($_SERVER['CONTENT_TYPE'])) ? $_SERVER['CONTENT_TYPE'] : @getenv('CONTENT_TYPE');
foreach ($_SERVER as $key => $val)
{
if (strncmp($key, 'HTTP_', 5) === 0)
{
$headers[substr($key, 5)] = $this->_fetch_from_array($_SERVER, $key, $xss_clean);
}
}
}
// take SOME_HEADER and turn it into Some-Header
foreach ($headers as $key => $val)
{
$key = str_replace('_', ' ', strtolower($key));
$key = str_replace(' ', '-', ucwords($key));
$this->headers[$key] = $val;
}
return $this->headers;
}
// --------------------------------------------------------------------
/**
* Get Request Header
* 返回请求头(request header)数组中某一个元素的值
* Returns the value of a single member of the headers class member
*
* @param string array key for $this->headers
* @param boolean XSS Clean or not
* @return mixed FALSE on failure, string on success
*/
public function get_request_header($index, $xss_clean = FALSE)
{
if (empty($this->headers))
{
$this->request_headers();
}
if ( ! isset($this->headers[$index]))
{
return FALSE;
}
if ($xss_clean === TRUE)
{
return $this->security->xss_clean($this->headers[$index]);
}
return $this->headers[$index];
}
// --------------------------------------------------------------------
/**
* Is ajax Request?
* 判断是否为ajax请求
* Test to see if a request contains the HTTP_X_REQUESTED_WITH header
*
* @return boolean
*/
public function is_ajax_request()
{
return ($this->server('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest');
}
// --------------------------------------------------------------------
/**
* Is cli Request?
* 判断是否来自cli请求
* Test to see if a request was made from the command line
*
* @return bool
*/
public function is_cli_request()
{
return (php_sapi_name() === 'cli' OR defined('STDIN'));
}
}
/* End of file Input.php */
/* Location: ./system/core/Input.php */
~~~
';
Security.php
最后更新于:2022-04-01 20:46:12
~~~
'[removed]',
'document.write' => '[removed]',
'.parentNode' => '[removed]',
'.innerHTML' => '[removed]',
'window.location' => '[removed]',
'-moz-binding' => '[removed]',
'' => '-->',
' '' => ''
);
/* never allowed, regex replacement */
/**
* List of never allowed regex replacement
* 不允许的正则替换字符串列表
* @var array
* @access protected
*/
protected $_never_allowed_regex = array(
'javascript\s*:',
'expression\s*(\(|&\#40;)', // CSS and IE
'vbscript\s*:', // IE, surprise!
'Redirect\s+302',
"([\"'])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?"
);
/**
* Constructor
*
* @return void
*/
public function __construct()
{
// Is CSRF protection enabled?
// csrf 是否开启
if (config_item('csrf_protection') === TRUE)
{
// CSRF config 读取CSRF 配置并赋值给本类下的对应的属性
foreach (array('csrf_expire', 'csrf_token_name', 'csrf_cookie_name') as $key)
{
if (FALSE !== ($val = config_item($key)))
{
$this->{'_'.$key} = $val;
}
}
// Append application specific cookie prefix
// 添加应用指定的cookie前缀
if (config_item('cookie_prefix'))
{
$this->_csrf_cookie_name = config_item('cookie_prefix').$this->_csrf_cookie_name;
}
// Set the CSRF hash
// 设置CSRF hash
$this->_csrf_set_hash();
}
log_message('debug', "Security Class Initialized");
}
// --------------------------------------------------------------------
/**
* Verify Cross Site Request Forgery Protection
* 验证跨站请求伪造保护
* @return object
*/
public function csrf_verify()
{
// If it's not a POST request we will set the CSRF cookie
// 如果不是post请求我们要设置 CSRF cookie
if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST')
{
return $this->csrf_set_cookie();
}
// Do the tokens exist in both the _POST and _COOKIE arrays?
// 如果请求令牌不存在,调用csrf_show_error 报错
if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name]))
{
$this->csrf_show_error();
}
// Do the tokens match?
// 如果令牌不对,报错。
if ($_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])
{
$this->csrf_show_error();
}
// We kill this since we're done and we don't want to
// polute the _POST array
// 销毁领牌子,因为我们不想污染_POST 数组
unset($_POST[$this->_csrf_token_name]);
// Nothing should last forever
// 销毁cookie名 并重新设置hash和cookie
unset($_COOKIE[$this->_csrf_cookie_name]);
$this->_csrf_set_hash();
$this->csrf_set_cookie();
log_message('debug', 'CSRF token verified');
return $this;
}
// --------------------------------------------------------------------
/**
* Set Cross Site Request Forgery Protection Cookie
* 设置伪造cookie保护跨站请求
* @return object
*/
public function csrf_set_cookie()
{
$expire = time() + $this->_csrf_expire;
$secure_cookie = (config_item('cookie_secure') === TRUE) ? 1 : 0;
// 当存在https时设置cookie
if ($secure_cookie && (empty($_SERVER['HTTPS']) OR strtolower($_SERVER['HTTPS']) === 'off'))
{
return FALSE;
}
// 设置cookie
setcookie($this->_csrf_cookie_name, $this->_csrf_hash, $expire, config_item('cookie_path'), config_item('cookie_domain'), $secure_cookie);
log_message('debug', "CRSF cookie Set");
return $this;
}
// --------------------------------------------------------------------
/**
* Show CSRF Error
* 显示CSRF错误
* @return void
*/
public function csrf_show_error()
{
show_error('The action you have requested is not allowed.');
}
// --------------------------------------------------------------------
/**
* Get CSRF Hash
* 获取CSRF hash
* Getter Method
*
* @return string self::_csrf_hash
*/
public function get_csrf_hash()
{
return $this->_csrf_hash;
}
// --------------------------------------------------------------------
/**
* Get CSRF Token Name
* 获取CSRF 令排名
* Getter Method
*
* @return string self::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 function 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.
* 对数据进行过滤,从而可以防止跨站脚本攻击。这个函数做了一些工作,但是它是非常彻底的,
* 甚至可以防止大多数模糊的XSS企图。当然,没有什么是100%安全的,
* 但是我们在这个过滤器中已经做了所欲我们所能做想到的了。
*
* Note: This function should only be used to deal with data
* upon submission. It's not something that should
* be used for general runtime processing.
* 注意:这个函数只能用来处理已经提交的数据。
* 在通常的运行过程中,不应该使用该函数。
*
* This function was based in part on some code and ideas I
* got from Bitflux: http://channel.bitflux.ch/wiki/XSS_Prevention
*
* 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:
* http://ha.ckers.org/xss.html
*
* @param mixed string or array
* @param bool
* @return string
*/
public function xss_clean($str, $is_image = FALSE)
{
/*
* Is the string an array?
* 需要过滤的字符串是数组吗?
*
*/
if (is_array($str))
{
while (list($key) = each($str))
{
$str[$key] = $this->xss_clean($str[$key]);
}
return $str;
}
/*
* Remove Invisible Characters
* 移除不可见字符
*/
$str = remove_invisible_characters($str);
// Validate Entities in URLs
// 验证在URL中的字符实体
$str = $this->_validate_entities($str);
/*
* URL Decode
* URL解码
* Just in case stuff like this is submitted:
* 防止下面这样的东西提交
* Google
*
* Note: Use rawurldecode() so it does not remove plus signs
* rawurldecode() 不会把加号('+')解码为空格,而 urldecode() 可以。
*
*/
$str = rawurldecode($str);
/*
* Convert character entities to ASCII
* 把实体字符转换成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-z]+=([\'\"]).*?\\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
* 这用来防止字符串类似于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.
* 注意:preg_replace在这里是极其缓慢的对于大块的数据,所以使用str_replace
*
*/
if (strpos($str, "\t") !== FALSE)
{
$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
* 使PHP标签安全
* Note: XML tags are inadvertently replaced too:
* 注意: XML标签也会被无意的替换
* '), 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', 'script', 'base64',
'applet', 'alert', 'document', 'write', 'cookie', 'window'
);
foreach ($words as $word)
{
$temp = '';
for ($i = 0, $wordlen = strlen($word); $i < $wordlen; $i++)
{
$temp .= substr($word, $i, 1)."\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($temp, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str);
}
/*
* Remove disallowed Javascript in links or img tags
* 移除在链接和图片标签中不允许的javascript代码
* We used to do some version comparisons and use of stripos for PHP5,
* 我们通常在PHP5中使用stripos做一些版本的比较,
* but it is dog slow compared to these simplified non-capturing
* 但是特别是当模式在字符串中存在的时候,它要比非捕获的preg_match()慢很多
* preg_match(), especially if the pattern exists in the string
*/
do
{
$original = $str;
if (preg_match("/]*?)(>|$)#si", array($this, '_js_link_removal'), $str);
}
if (preg_match("/]*?)(\s?/?>|$)#si", array($this, '_js_img_removal'), $str);
}
if (preg_match("/script/i", $str) OR preg_match("/xss/i", $str))
{
$str = preg_replace("#<(/*)(script|xss)(.*?)\>#si", '[removed]', $str);
}
}
while($original != $str);
unset($original);
// Remove evil attributes such as style, onclick and xmlns
// 移除有害的属性,比如style , onclick , xmlns
$str = $this->_remove_evil_attributes($str, $is_image);
/*
* Sanitize naughty HTML elements
* 清理不适当的HTML元素
* If a tag containing any of the words in the list
* below is found, the tag gets converted to entities.
* 如果一个标签包含任何一个下面列表中的单词,这个标签转换为一个字符实体
* So this: