PbootCMS经常被黑怎么办?代码级修改防注入防XSS解决方案!

关于本方案安全级别说明
PbootCMS代码漏洞和安全方面目前做的非常不到位,用于正常使用环境中往往不可避免的遇到入侵的情况,我接触这个系统以来,看到很多朋友遇到了这个问题,因此将这个方法分享出来,希望能帮助到广大同行以及需要的朋友,特别是没有任何代码经验的站长,至少在代码修补方面给予一些无私分享,作为长期操作网络推广和网站打交道的老手,我认为最能解决网站被入侵的方法唯有全站静态生成,然后将所有PHP文件隐藏起来,只让浏览网站的人看到纯粹的HTML页面,就算是高手,也需要有将服务器拿下的能力才有可能入侵成功,只要服务器防护到位,网站就很难被入侵,我自己可以实现PbootCMS代码修改的这一复杂操作,但是因为需要付出较大的劳动量,所以我私藏了,目前我已经将这一方案应用在给客户进行网站建设的服务中,但是这种修改对于不懂代码的人来说门槛太高,所以我研究了一种能相对便于普通人进行代码调整的策略,至少能屏蔽一些直接用工具就能批量入侵的情况,另外这一方法还需要配合文件权限以及其他设置才能达到最佳效果,如果安全等级有3个等级的话,这个方法应该属于中级了,大多数做网站建设的个人能达到这一级别差不多就算及格了。
重要声明
1:PbootCMS 的核心文件修改需谨慎,修改前务必做好文件备份,避免系统异常,受限于单篇文章长度篇幅,方案还有文件需要修改,可以继续查阅本栏目的下一篇文章,本文先解决严重的注入问题,再逐步解决其他程序的漏洞,本文是给普通站长看的,欢迎高手进一步优化我的方案;
2:防注入不能仅依赖单一文件修改,需结合输入过滤、输出转义、数据库参数绑定等多层防护;
3:以下修改基于通用安全防护逻辑,需根据你使用的 PbootCMS 版本(不同版本 helper.php 结构可能不同)适配,若你能提供具体的 helper.php 文件内容,可精准调整。
核心思路
1:/core/function/helper.php 是 PbootCMS 的全局辅助函数文件,强化参数过滤需:
2:统一过滤 GET/POST/COOKIE 等输入参数,拦截恶意 SQL 注入、XSS 等 payload;
3:对数据库查询相关的辅助函数增加参数绑定 / 转义;
4:新增通用过滤函数,覆盖全局参数调用场景。
具体修改方案(以V3.2.12版 helper.php 为例)
步骤 1:新增通用安全过滤函数
在/core/function/helper.php中新增以下函数(建议放在文件末尾、已有函数之外),用于统一过滤参数:
/**
* 全局参数安全过滤(防SQL注入+XSS)
* @param mixed $data 要过滤的参数(单个值/数组)
* @param bool $isDb 是否为数据库查询参数(开启数据库转义)
* @return mixed
*/
function safe_filter($data, $isDb = true) {
if (is_array($data)) {
// 递归过滤数组
foreach ($data as $k => $v) {
$data[$k] = safe_filter($v, $isDb);
}
return $data;
} else {
$data = trim($data);
// 1. 过滤XSS(基础层,建议结合前端过滤)
$data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
// 2. 防SQL注入(核心:数据库转义,避免直接拼接SQL)
if ($isDb && function_exists('mysqli_real_escape_string')) {
// 需引入数据库连接对象(根据PbootCMS的数据库配置调整)
global $db;
if (isset($db) && $db->link) {
$data = mysqli_real_escape_string($db->link, $data);
}
}
// 3. 拦截常见注入关键字(补充防护,可根据业务调整)
$inject_pattern = '/union|select|insert|update|delete|drop|alter|truncate|exec|xp_cmdshell|into|outfile|load_file/i';
$data = preg_replace($inject_pattern, '', $data);
return $data;
}
}
/**
* 全局获取参数(替代原生$_GET/$_POST,强制过滤)
* @param string $key 参数名
* @param string $type 来源:get/post/cookie/all
* @param mixed $default 默认值
* @param bool $isDb 是否为数据库查询参数
* @return mixed
*/
function get_safe_param($key, $type = 'all', $default = '', $isDb = true) {
$value = $default;
switch ($type) {
case 'get':
$value = isset($_GET[$key]) ? $_GET[$key] : $default;
break;
case 'post':
$value = isset($_POST[$key]) ? $_POST[$key] : $default;
break;
case 'cookie':
$value = isset($_COOKIE[$key]) ? $_COOKIE[$key] : $default;
break;
case 'all':
$value = isset($_REQUEST[$key]) ? $_REQUEST[$key] : $default;
break;
}
return safe_filter($value, $isDb);
}
步骤 2:修改原有参数获取函数(关键)
找到helper.php中原有获取参数的函数(如get_param/request_param等,不同版本命名可能不同),替换为强制调用安全过滤函数:
原函数示例(需替换):
function get_param($key, $default = '') {
return isset($_REQUEST[$key]) ? $_REQUEST[$key] : $default;
}
修改后:
function get_param($key, $default = '', $isDb = true) {
// 强制调用安全过滤函数
return get_safe_param($key, 'all', $default, $isDb);
}
步骤 3:强化数据库查询辅助函数(若有,后面会给到这个文件的完整修改文件,这个只是参考说明,帮助大家理解)
若helper.php中有数据库查询相关函数(如db_query),修改为强制参数绑定 / 转义:
示例修改:
function db_query($sql, $params = []) {
global $db;
// 1. 过滤SQL语句中的恶意关键字(基础防护)
$sql = preg_replace('/union|select|insert|update|delete|drop/i', '', $sql);
// 2. 参数绑定(核心:避免直接拼接参数)
if (!empty($params)) {
foreach ($params as $k => $v) {
$v = safe_filter($v, true);
$sql = str_replace(':'.$k, $v, $sql);
}
}
return $db->query($sql);
}
额外防护建议(必做)
禁用危险函数:在php.ini中禁用eval、exec、system等危险函数;
输出转义:前端展示数据时,对用户输入内容再次使用htmlspecialchars转义,防 XSS;
数据库层防护:使用 PDO 预处理语句 / 参数绑定,替代直接 SQL 拼接(PbootCMS 新版已支持,需确认);
权限控制:限制网站目录的写入权限,禁止非必要文件执行;
版本更新:及时升级 PbootCMS 到最新版,官方会修复已知安全漏洞;
日志监控:新增参数过滤日志,记录异常参数(如包含注入关键字的请求),便于排查。
注意事项
1:若修改后出现业务异常(如正常参数被过滤),需调整inject_pattern正则规则,避免误拦截;
2:mysqli_real_escape_string依赖数据库连接,需确保$db对象在helper.php中可正常调用(不同版本 PbootCMS 的数据库连接变量名可能不同,如$mysqli);
3:测试:修改后需测试登录、表单提交、数据查询等功能,确保无兼容问题。
最后给出按照这个思路修改的完整文件,可以直接复制粘贴,但请注意原有文件的备份:
<?php
/**
* @copyright (C)2016-2099 Hnaoyun Inc.
* @author XingMeng
* @email hnxsh@foxmail.com
* @date 2017年11月5日
* 助手函数
* 【安全强化】新增全局参数过滤函数,强化原有参数获取逻辑,防SQL注入/XSS
*/
use core\basic\Config;
use core\basic\Json;
use core\view\View;
use core\view\Paging;
use core\basic\Response;
use core\basic\Url;
use core\basic\Basic;
use core\basic\Smtp;
/**
* 生成实际跳转路径
*
* @param string $url
* 接收控制器方法访问完整路径,如:/home/index/index
* @return mixed
*/
function url($url, $suffix = false)
{
return Url::get($url, $suffix);
}
function isPositiveInteger($str) {
return preg_match('/^[1-9]\d*$/', $str) === 1;
}
/**
* 生成前端路径
*
* @param string $url
* 前端地址参数
* @return mixed
*/
function homeurl($url, $suffix = null, $qs = null)
{
return Url::home($url, $suffix, $qs);
}
/**
* 自定义错误页面
*
* @param string $_string内容
* @param string $_url跳转地址
* @param number $_time时间
*/
function error($string, $jump_url = null, $time = 2)
{
@ob_clean();
http_response_code(404);
if (! $string)
$string = '未知错误!';
if ($jump_url == '-1' && isset($_SERVER['HTTP_REFERER'])) {
$jump_url = $_SERVER['HTTP_REFERER'];
if (strpos($jump_url, get_http_url()) !== 0) {
$jump_url = '/';
}
} elseif ($jump_url == '-1') {
$jump_url = null;
}
if (Config::get('return_data_type') == 'json' || is_ajax()) { // 接口模型返回格式数据
Response::json(0, strip_tags($string), $jump_url);
} else {
$err_tpl = CORE_PATH . '/template/error.html';
echo parse_info_tpl($err_tpl, $string, $jump_url, $time);
}
exit();
}
/**
* 自定义错误页面
*
* @param string $_string内容
* @param string $_url跳转地址
* @param number $_time时间
*/
function success($string, $jump_url = null, $time = 2)
{
if ($jump_url == '-1' && isset($_SERVER['HTTP_REFERER'])) {
$jump_url = $_SERVER['HTTP_REFERER'];
if (strpos($jump_url, get_http_url()) !== 0) {
$jump_url = '/';
}
} elseif ($jump_url == '-1') {
$jump_url = null;
}
if (Config::get('return_data_type') == 'json' || is_ajax()) { // 接口模型返回格式数据
Response::json(1, strip_tags($string), $jump_url);
} else {
$err_tpl = CORE_PATH . '/template/success.html';
echo parse_info_tpl($err_tpl, $string, $jump_url, $time);
}
exit();
}
/**
* 弹窗
*
* @param string $info信息
*/
function alert($info, $status = 0)
{
if (Config::get('return_data_type') == 'json' || is_ajax()) { // 接口模型返回格式数据
Response::json($status, strip_tags($info));
} else {
echo '<script type="text/javascript">alert("' . clear_html_blank($info) . '");</script>';
}
}
/**
* 弹窗并返回前页
*
* @param string $info信息
*/
function alert_back($info, $status = 0)
{
if (Config::get('return_data_type') == 'json' || is_ajax()) { // 接口模型返回格式数据
Response::json($status, strip_tags($info));
} else {
echo '<script type="text/javascript">alert("' . clear_html_blank($info) . '");window.history.go(-1);</script>';
exit();
}
}
/**
* 跳转
*
* @param string $url跳转地址
*/
function location($url)
{
if ($url == '-1' && isset($_SERVER['HTTP_REFERER'])) {
$url = $_SERVER['HTTP_REFERER'];
if (strpos($url, get_http_url()) !== 0) {
$url = '/';
}
}
header('Location:' . $url);
exit();
}
/**
* 弹窗并跳转
*
* @param string $info信息
* @param string $url跳转地址
*/
function alert_location($info, $url, $status = 0)
{
if ($url == '-1' && isset($_SERVER['HTTP_REFERER'])) {
$url = $_SERVER['HTTP_REFERER'];
if (strpos($url, get_http_url()) !== 0) {
$url = '/';
}
}
if (Config::get('return_data_type') == 'json' || is_ajax()) { // 接口模型返回格式数据
Response::json($status, strip_tags($info), $url);
} else {
echo '<script type="text/javascript">alert("' . clear_html_blank($info) . '");location.href="' . $url . '";</script>';
exit();
}
}
/**
* 弹窗并关闭
*
* @param string $info信息
*/
function alert_close($info, $status = 0)
{
if (Config::get('return_data_type') == 'json' || is_ajax()) { // 接口模型返回格式数据
Response::json($status, strip_tags($info));
} else {
echo '<script type="text/javascript">alert("' . clear_html_blank($info) . '");window.close();</script>';
exit();
}
}
/**
* 实例化模型对象助手
*
* @param string $name
* 需要实力化的模型名称
* @param string $new
* 是否强制新建对象
* @return mixed
*/
function model($name = null, $new = false)
{
return Basic::createModel($name, $new);
}
/**
* api读取数据
*
* @param string $name
* 接口名称,如:add或 admin.user.addd
* @param array $param
* 参数
* @param string $rsOriginal
* 结果不处理直接返回
* @param string $jsonRsArray
* 返回Json数组方式
* @return mixed
*/
function api($args = null)
{
return Basic::createApi(func_get_args());
}
// 输出模板内容
function display($tpl)
{
$view = View::getInstance();
echo $view->parser($tpl);
}
// 解析模板内容
function parser($tpl)
{
$view = View::getInstance();
return $view->parser($tpl);
}
// 设置模板
function set_theme($theme_name)
{
$view = View::getInstance();
$view->assign('theme', $theme_name);
}
// 注入模板变量
function assign($var, $value)
{
$view = View::getInstance();
$view->assign($var, $value);
}
// 变量获取接口
function get_var($var)
{
$view = View::getInstance();
return $view->getVar($var);
}
// 手动生成分页信息,返回限制语句
function page($tatal, $morePageStr = false)
{
$page = Paging::getInstance();
return $page->limit($tatal, $morePageStr);
}
// 内容输出助手函数
function response($data)
{
return core\basic\Response::handle($data);
}
// Json内容输出助手函数
function json($code, $data, $tourl = null)
{
return core\basic\Response::json($code, $data, $tourl);
}
/**
* 数据过滤
*
* @param mixed $varname
* 字符串或参数名称
* @param array $condition
* array('d_source'=>'post','d_none'=>true,'d_require'=>true,'d_type'=>'int','d_max'=>5,'d_min'=2,'d_default'=>'')
* 字段名称:$varname,用字段名称做key传递文本
* 数据源:d_source,[post、get、cookie、session、both、string]
* 是否允许空:d_none,[true、false],如果为false接受的收据为空,则直接报错
* 是否必须:d_require,[true、false],如果为true意味着如果数据不满足要求直接报错,否则返回null
* 数据类型:d_type,[int、float、num、letter、var、bool、date、array、object]
* 正则表达:d_regular,接受正则表达式
* 最大值|最大长度:d_max
* 最小值|最小长度:d_min
* 默认值:d_default
* @return mixed
*/
function filter($varname, $condition)
{
// 变量名称文本
if (array_key_exists($varname, $condition) && $condition[$varname]) {
$vartext = $condition[$varname];
} else {
$vartext = $varname;
}
// 数据源
if (array_key_exists('d_source', $condition)) {
switch ($condition['d_source']) {
case 'post':
$data = @$_POST[$varname];
break;
case 'get':
$data = @$_GET[$varname];
break;
case 'cookie':
$data = @$_COOKIE[$varname];
break;
case 'session':
$data = session($varname);
break;
case 'both':
$data = @$_POST[$varname] ?: @$_GET[$varname];
break;
case 'string':
$data = $varname;
default:
error($vartext . '数据获取方式设置错误!');
}
// 去空格
if (is_string($data))
$data = trim($data);
} else {
$data = $varname; // 没有数据源指定时直接按照字符串过滤处理
}
// 数据为空时,进行是否允许空检测
if (! $data && array_key_exists('d_none', $condition) && $condition['d_none'] === false) {
error($vartext . '不能为空!');
}
// 判断是否强制检测,为true时,意味着如果数据不满足要求直接报错,否则返回null
if (array_key_exists('d_require', $condition) && $condition['d_require'] == true) {
$require = true;
} else {
$require = false;
}
// 数据类型检测
if (array_key_exists('d_type', $condition)) {
switch ($condition['d_type']) {
case 'int':
if (! preg_match('/^[0-9]+$/', $data)) {
$err = '必须为整数!';
}
break;
case 'float':
if (! is_float($data)) {
$err = '必须为浮点数!';
}
break;
case 'num':
if (! is_numeric($data)) {
$err = '必须为数字!';
}
break;
case 'letter':
if (! preg_match('/^[a-zA-Z]+$/', $data)) {
$err = '只能包含字母!';
}
break;
case 'var':
if (! preg_match('/^[\w\-\.]+$/', $data)) {
$err = '只能包含字母、数字、划线、点!';
}
break;
case 'bool':
if (! is_bool($data)) {
$err = '必须为布尔类型!';
}
break;
case 'date':
if (! strtotime($data)) {
$err = '必须为日期类型!';
}
break;
case 'array':
if (! is_array($data)) {
$err = '必须为数组类型!';
}
break;
case 'object':
if (! is_object($data)) {
$err = '必须为对象类型!';
}
break;
case 'vars':
if (! preg_match('/^[\x{4e00}-\x{9fa5}\w\-\.,\s]+$/u', $data)) {
$err = '只能包含中文、字母、数字、横线、点、逗号、空格!';
}
break;
default:
if ($condition['d_type'])
error($vartext . '数据类型设置错误!');
}
}
// 非必须或必须但无错误时执行
if ((! $require || ($require && ! isset($err)))) {
// 正则匹配
if (array_key_exists('d_regular', $condition)) {
if (! preg_match($condition['d_regular'], $data)) {
$err = '不符合正则表达式规则!';
}
}
// 最大值匹配
if (array_key_exists('d_max', $condition)) {
if (is_numeric($data)) {
if ($data > $condition['d_max']) {
$err = '不能大于' . $condition['d_max'];
}
} else {
if (mb_strlen($data) > $condition['d_max']) {
$err = '长度不能大于' . $condition['d_max'];
}
}
}
// 最小值匹配
if (array_key_exists('d_min', $condition)) {
if (is_numeric($data)) {
if ($data < $condition['d_min']) {
$err = '不能小于' . $condition['d_min'];
}
} else {
if (mb_strlen($data) < $condition['d_min']) {
$err = '长度不能小于' . $condition['d_min'];
}
}
}
}
// 如果为必须且有错误,则显示错误,如果非必须,但有错误,则设置数据为null
if ($require && isset($err)) {
error($vartext . $err);
} elseif (isset($err)) {
$data = null;
}
// 如果设置有默认值,默认值
if (array_key_exists('d_default', $condition)) {
$data = (! is_null($data)) ? $data : $condition['d_default'];
}
if (is_string($data)) {
$data = trim($data); // 去空格
$data = preg_replace_r('/(x3c)|(x3e)/', '', $data); // 去十六进制括号
$data = preg_replace_r('/pboot:if/i', 'pboot@if', $data); // 过滤插入cms条件语句
$data = preg_replace_r('/pboot:sql/i', 'pboot@sql', $data); // 过滤插入cms条件语句
$data = preg_replace_r('/GET\[/i', 'GET@[', $data);
$data = preg_replace_r('/POST\[/i', 'POST@[', $data);
}
// 【安全强化】新增:调用全局安全过滤函数,强化SQL注入/XSS防护
$data = safe_filter($data, true);
// 销毁错误
unset($err);
// 返回收据
return escape_string($data);
}
/**
* 获取GET参数
*
* @param string $name
* 参数名称
* @param mixed $type
* 数据类型
* @param string $require
* 是否为必须,为true是,如果不满足条件直接错误
* @param string $vartext
* 变量描述文本
* @param string $default
* 在非必需情况下默认值
* @return mixed
*/
function get($name, $type = null, $require = false, $vartext = null, $default = null)
{
$condition = array(
'd_source' => 'get',
'd_type' => $type,
'd_require' => $require,
$name => $vartext,
'd_default' => $default
);
return filter($name, $condition);
}
/**
* * 获取POST参数
*
* @param string $name
* 参数名称
* @param mixed $type
* 数据类型
* @param string $require
* 是否为必须,为true是,如果不满足条件直接错误
* @param string $vartext
* 变量描述文本
* @param string $default
* 在非必需情况下默认值
* @return mixed
*/
function post($name, $type = null, $require = false, $vartext = null, $default = null)
{
$condition = array(
'd_source' => 'post',
'd_type' => $type,
'd_require' => $require,
$name => $vartext,
'd_default' => $default
);
return filter($name, $condition);
}
/**
* * 获取参数,post或get
*
* @param string $name
* 参数名称
* @param mixed $type
* 数据类型
* @param string $require
* 是否为必须,为true是,如果不满足条件直接错误
* @param string $vartext
* 变量描述文本
* @param string $default
* 在非必需情况下默认值
* @return mixed
*/
function request($name, $type = null, $require = false, $vartext = null, $default = null)
{
if (isset($_POST[$name])) {
$d_source = 'post';
} else {
$d_source = 'get';
}
$condition = array(
'd_source' => $d_source,
'd_type' => $type,
'd_require' => $require,
$name => $vartext,
'd_default' => $default
);
return filter($name, $condition);
}
//过滤一下代码
function removePhpCode($string, $clearjs = true){
$string = htmlspecialchars_decode($string, ENT_QUOTES);
$string = preg_replace('/<\?php.*?\?>/is', '', $string);// 移除PHP
$string = preg_replace('/<\?.*?\?>/is', '', $string);// 移除短PHP
$string = preg_replace('/<%.*?%>/is', '', $string);//移除ASP
if($clearjs == true){
$string = preg_replace('/<script.*?>.*?<\/script>/is', '', $string);// 移除JS
}
$string = htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
return $string;
}
function cleanUrlString($str) {
$str = htmlspecialchars_decode($str, ENT_QUOTES);
$str = trim($str);
$str = preg_replace('/[^a-zA-Z0-9\s-]/', '', $str);
$str = preg_replace('/\s+/', '-', $str);
$str = preg_replace('/-{2,}/', '-', $str);
$str = preg_replace('/-+/', '-', $str);
$str = trim($str, '-');
$str = strtolower($str);
return $str;
}
function testforbWords($str){
$testwords = array('%7B','%7D','{','}','%5B','%5D','[',']','%28','%29','(',')','%27',"'",'%2A','*','%3C','%3E','<','>','%3B',';','%22','"','%60','`','\\x');
foreach($testwords as $value){
if(stripos($str,$value) !== false){
location(Url::home('/'));
}
}
}
function testforbSearch($str){
$testwords = array('select','insert','update','delete','where','drop','--','create','truncate','rename','execute','union',"\r","\n","\r\n");
foreach($testwords as $value){
if(stripos($str,$value) !== false){
location(Url::home('/'));
}
}
}
/**
* 读取或写入Cookie信息
*
* @param string $name
* 名称
* @param string $value
* 值
* @param int $expire
* 秒数
* @param string $path
* 路径,默认站点目录
*/
function cookie($name, $value = null, $expire = null, $path = null, $domain = null, $secure = null, $httponly = false)
{
if (! is_null($value)) {
$path = SITE_DIR . '/';
if (is_string($value))
$value = trim($value);
// 【安全强化】新增:写入Cookie前过滤参数,防XSS/SQL注入
$value = safe_filter($value, false);
$_COOKIE[$name] = $value; // 让cookie立即生效
if (! is_null($expire)) {
return setcookie($name, $value, time() + $expire, $path, $domain, $secure, $httponly);
} else {
return setcookie($name, $value, 0, $path, $domain, $secure, $httponly);
}
} else {
if (isset($_COOKIE[$name])) {
// 【安全强化】新增:读取Cookie时过滤参数,防XSS/SQL注入
$cookie_val = safe_filter($_COOKIE[$name], false);
return escape_string($cookie_val);
} else {
return null;
}
}
}
/**
* 读取或写入session信息
*
* @param string $name
* 支持点分多级获取
* @param mixed $value
* 设置值
* @return string|NULL|unknown
*/
function session($name, $value = null)
{
if (! isset($_SESSION)) {
session_start(); // 自动启动会话
}
if (! is_null($value)) {
// 【安全强化】新增:写入Session前过滤参数,防XSS/SQL注入
$value = safe_filter($value, false);
if (isset($_SESSION[$name])) {
if ($_SESSION[$name] != $value) {
$_SESSION[$name] = $value;
}
} else {
$_SESSION[$name] = $value;
}
return $value;
} else {
if (strpos($name, '.')) {
if (isset($_SESSION[$name])) {
// 【安全强化】新增:读取Session时过滤参数,防XSS/SQL注入
$session_val = safe_filter($_SESSION[$name], false);
return $session_val;
}
$names = explode('.', $name);
if (! isset($_SESSION[$names[0]])) {
return null;
}
$var = $_SESSION[$names[0]];
$len = count($names);
for ($i = 1; $i < $len; $i ++) {
if (is_array($var)) {
if (isset($var[$names[$i]])) {
$var = $var[$names[$i]];
} else {
return null;
}
} elseif (is_object($var)) {
if (isset($var->{$names[$i]})) {
$var = $var->{$names[$i]};
} else {
return null;
}
} else {
break;
}
}
// 【安全强化】新增:读取多级Session时过滤参数,防XSS/SQL注入
$var = safe_filter($var, false);
return $var;
} else {
if (isset($_SESSION[$name])) {
// 【安全强化】新增:读取Session时过滤参数,防XSS/SQL注入
$session_val = safe_filter($_SESSION[$name], false);
return $session_val;
} else {
return null;
}
}
}
}
// 检查会话参数是否存在
function issetSession($name)
{
if (! isset($_SESSION)) {
session_start(); // 自动启动会话
}
return isset($_SESSION[$name]);
}
/**
* 快速发送邮件函数
*
* @param array $config
* 邮件服务器连接数组,需包含 smtp_server、smtp_username、smtp_password、smtp_port、smtp_port
* @param string $to
* 邮件接收人
* @param string $subject
* 邮件主题
* @param string $body
* 邮件正文
*/
function sendmail(array $config, $to, $subject, $body)
{
// 【安全强化】新增:过滤邮件参数,防注入/恶意内容
$to = safe_filter($to, false);
$subject = safe_filter($subject, false);
$body = safe_filter($body, false);
$smtp = new Smtp($config['smtp_server'], $config['smtp_username'], $config['smtp_password'], $config['smtp_port'], $config['smtp_ssl']);
if ($smtp->sendMail($to, $subject, $body)) {
return true;
} else {
return $smtp->error();
}
}
// 发送短信
function sendsms(array $config, $to, $content)
{
// 【安全强化】新增:过滤短信参数,防注入/恶意内容
$to = safe_filter($to, false);
$content = safe_filter($content, false);
if (! $to || ! $content) {
return false;
}
if (! isset($config['sms_account']) || ! isset($config['sms_pwd']) || ! isset($config['sms_signid'])) {
alert_back('短信发送参数配置有误');
}
$data['Account'] = $config['sms_account'];
$data['Pwd'] = $config['sms_pwd'];
$data['SignId'] = $config['sms_signid'];
$data['Content'] = $content;
$to = str_replace("\r\n", ",", $to); // 替换回车
$to = str_replace(",", ",", $to); // 替换中文逗号分割符
$data['Mobile'] = str_replace(" ", "", $to); // 替换空格
$url = "http://api.feige.ee/SmsService/Send";
if (! ! $res = get_url($url, $data)) {
$result = json_decode($res);
if (! ($result->Code === 0)) {
error('短信发送失败,' . $result->Message);
} else {
return true;
}
}
}
// 查询短信余额
function get_sms_balance(array $config)
{
$data['Account'] = $config['sms_account'];
$data['Pwd'] = $config['sms_pwd'];
$url = "http://api.feige.ee/Account/Balance";
if (! ! $res = get_url($url, $data)) {
$result = json_decode($res);
if (! ($result->Code === 0)) {
error('查询失败,' . $result->Message);
} else {
return $result->Balance;
}
}
}
// 返回404页面,文件中可使用{info}替换提示信息
function _404($string, $jump_url = null, $time = 2)
{
http_response_code(404);
$file_404 = ROOT_PATH . '/404.html';
if (file_exists($file_404)) {
echo parse_info_tpl($file_404, $string, $jump_url, $time);
exit();
} else {
error($string, $jump_url, $time);
}
}
// php对象转为数组
function toArray($obj){
if($obj === null){
return [];
}else{
return json_decode(json_encode($obj),true);
}
}
//if标签比较符处理
function symbol($matches): string
{
$flag = '';
$symbol1 = ['&&','||'];
foreach ($symbol1 as $items) {
if (strpos($matches, $items) !== false) {
$arr = explode($items, $matches);
switch ($items) {
case '&&':
$bool1 = compareSymbol1($arr[0]);
$bool2 = compareSymbol1($arr[1]);
$flag = $bool1 && $bool2 ? 'if' : 'else';
break;
case '||':
$bool1 = compareSymbol1($arr[0]);
$bool2 = compareSymbol1($arr[1]);
$flag = $bool1 || $bool2 ? 'if' : 'else';
break;
}
break;
}
}
if(!$flag){
$compare = compareSymbol1($matches);
if($compare === true){
$flag = 'if';
}else if($compare === false){
$flag = 'else';
}
}
return $flag;
}
function compareSymbol1($str){
$bool = null;
$symbol = ['>=','<=','!=','==','>','<'];
foreach ($symbol as $items) {
if (strpos($str, $items) !== false) {
$arr = explode($items, $str);
switch ($items) {
case '>':
$res1 = compareSymbol2($arr[0]);
$res2 = compareSymbol2($arr[1]);
$bool = $res1 > $res2;
break;
case '>=':
$res1 = compareSymbol2($arr[0]);
$res2 = compareSymbol2($arr[1]);
$bool = $res1 >= $res2;
break;
case '!=':
$res1 = compareSymbol2($arr[0]);
$res2 = compareSymbol2($arr[1]);
$bool = $res1 != $res2;
break;
case '==':
$res1 = compareSymbol2($arr[0]);
$res2 = compareSymbol2($arr[1]);
$bool = $res1 == $res2;
break;
case '<=':
$res1 = compareSymbol2($arr[0]);
$res2 = compareSymbol2($arr[1]);
$bool = $res1 <= $res2;
break;
case '<':
$res1 = compareSymbol2($arr[0]);
$res2 = compareSymbol2($arr[1]);
$bool = $res1 < $res2;
break;
}
break;
}
}
if($bool === null){
$res = compareSymbol2($str);
if (trim($res)) {
return true;
} else {
return false;
}
}
return $bool;
}
function compareSymbol2($str){
$res = null;
$symbol2 = ['%'];
foreach ($symbol2 as $items) {
if (strpos($str, $items) !== false) {
$arr = explode($items, $str);
if ($items == '%') {
$res = $arr[0] % $arr[1];
}
break;
}
}
if($res === null) {
$str = trim($str);
$str = trim($str,"'");
$res = (string)$str;
}
return $res;
}
/**
* 【安全强化】全局参数安全过滤(防SQL注入+XSS)
* @param mixed $data 要过滤的参数(单个值/数组)
* @param bool $isDb 是否为数据库查询参数(开启数据库转义)
* @return mixed
*/
function safe_filter($data, $isDb = true) {
if (is_array($data)) {
// 递归过滤数组,防止数组传参绕过过滤
foreach ($data as $k => $v) {
$data[$k] = safe_filter($v, $isDb);
}
return $data;
} else {
if (!is_string($data) && !is_numeric($data)) {
return $data;
}
$data = trim($data);
// 过滤XSS,转义特殊字符(核心防XSS)
$data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
// 防SQL注入:数据库转义特殊字符(核心防注入)
if ($isDb && function_exists('mysqli_real_escape_string')) {
global $db;
if (isset($db) && $db->link) {
$data = mysqli_real_escape_string($db->link, $data);
}
}
// 拦截常见注入关键字,补充防护
$inject_pattern = '/union|select|insert|update|delete|drop|alter|truncate|exec|xp_cmdshell|into|outfile|load_file/i';
$data = preg_replace($inject_pattern, '', $data);
return $data;
}
}
/**
* 【安全强化】全局获取参数(替代原生$_GET/$_POST,强制过滤)
* @param string $key 参数名
* @param string $type 来源:get/post/cookie/all
* @param mixed $default 默认值
* @param bool $isDb 是否为数据库查询参数
* @return mixed
*/
function get_safe_param($key, $type = 'all', $default = '', $isDb = true) {
$value = $default;
switch ($type) {
case 'get':
$value = isset($_GET[$key]) ? $_GET[$key] : $default;
break;
case 'post':
$value = isset($_POST[$key]) ? $_POST[$key] : $default;
break;
case 'cookie':
$value = isset($_COOKIE[$key]) ? $_COOKIE[$key] : $default;
break;
case 'all':
$value = isset($_REQUEST[$key]) ? $_REQUEST[$key] : $default;
break;
}
return safe_filter($value, $isDb);
}
网站建设