我亏一点 - PHP
type: channel
link: https://000081.xyz/index.php/category/PHP
items: 苹果cms10 去除官方更新
items: 苹果cms10 挂码手机端自动跳转非法网站处理
items: 苹果cms10 列表和搜索过滤会员组没有权限的数据
items: PHP多级分类数据列表转为树型父子结构
items: ThinkPHP6 跨域开起后某些接口请求报错跨域
items: ThinkPHP6 开起跨域
items: ThinkPHP6 使用JwtToken
items: PHP pdo连接mysql数据库查询中文字符为？记录解决
items: PHP 微信公众号对接学习记录
items: PHP生成随机验证码

苹果cms10 去除官方更新
type: item
date: 2026-03-15T12:00:00+00:00
description: AI摘要 本文详细介绍了如何从苹果cms10中去除官方更新，以应对官方更新通道可能存在的安全风险。主要步骤包括：从GitHub下载源代码，批量替换官方域名为空或自定义域名，注释掉`Update.php`和`Safety.php`中的更新后门代码，移除播放器文件中的广告链接，以及注释掉多个JavaScript文件中的更新检测和请求代码。此外，还建议在服务器层面屏蔽官方域名，以彻底杜绝潜在的安全隐患。 此内容根据文章生成，仅用于文章内容的解释与总结 苹果cms10 去除官方更新根据奇安信实锤，基本上可以确认是官方更新通道投毒https://mp.weixin.qq.com/s/nl_Pntpoys9NVBp7RKLJvg1.下载源代码从github中下载源代码，一般来说后门不会直勾勾的写在github上，都是通过安装之后通过自动更新加入到代码中。所以此时的github代码是安全的https://github.com/magicblack/maccms102.批量替换域名（关键）使用代码编辑器，批量替换代码域名，此处域名我以自己网站代替，即使自己的域名有些请求失败，但是也好过官方的。union.maccms.la => 所有带有该域名的链接全部改为空maccms.la => 替换成自己的域名aHR0cDovL3VwZGF0ZS5tYWNjbXMubGEv => 该字符串为更新域名的base64编码，替换为空，有两处地方要修改。application/admin/controller/Update.phpapplication/admin/controller/Safety.php3.注释更新后门文件（关键）文件路径：application/admin/controller/Update.php<?php namespace app\admin\controller; use think\Db; use app\common\util\PclZip; class Update extends Base { var $_url; var $_save_path; public function __construct() { parent::__construct(); //header('X-Accel-Buffering: no'); $this->_url = base64_decode("")."v10/"; $this->_save_path = './application/data/update/'; } public function index() { return $this->fetch('admin@test/index'); } public function step1($file='') { // if(empty($file)){ // return $this->error(lang('param_err')); // } // $version = config('version.code'); // $url = $this->_url .$file . '.zip?t='.time(); // echo $this->fetch('admin@public/head'); // echo " ".lang('admin/update/step1_a')." ".lang('admin/update/step1_b')."\n"; // ob_flush();flush(); // sleep(1); // $save_file = $version.'.zip'; // $html = mac_curl_get($url); // @fwrite(@fopen($this->_save_path.$save_file,'wb'),$html); // if(!is_file($this->_save_path.$save_file)){ // echo lang('admin/update/download_err')."\n"; // exit; // } // if(filesize($this->_save_path.$save_file) <1){ // @unlink($this->_save_path.$save_file); // echo lang('admin/update/download_err')."\n"; // exit; // } // echo lang('admin/update/download_ok')."\n"; // echo lang('admin/update/upgrade_package_processed')."\n"; // ob_flush(…

苹果cms10 挂码手机端自动跳转非法网站处理
type: item
date: 2026-03-15T06:12:00+00:00
description: AI摘要 苹果cms10手机端自动跳转非法网站，经证实是官方更新通道投毒。恶意链接包括播放器引用`union.maccms.la`和`application/extra/active.php`文件，后者加载伪装的恶意JS，主要针对移动端。处理方法：1. 屏蔽官方更新域名`update.maccms.la`，将代码中相关链接置空或注释`Update.php`和`Safety.php`中的更新方法。2. 批量替换所有`maccms.la`域名。3. 删除`application/extra/active.php`文件。 此内容根据文章生成，仅用于文章内容的解释与总结 苹果cms10 挂码手机端自动跳转非法网站处理{alert type="info"}程序使用的是maccms.la的苹果cms，记录目前处理方法，不一定有效，可能还有漏网之鱼。{/alert}{message type="error" content="2026/3/15 17:00 实测此方法并不能完全解决，还有漏网之鱼，手机端还是偶尔会跳转"/}去更新可以查看这篇 https://www.000081.xyz/index.php/archives/388/{message type="success" content="2026/3/15 18:00 请从github下载最新的程序，进行域名替换，去除官方更新，然后再导入数据库数据，此方法目前有效"/}根据奇安信实锤，基本上可以确认是官方更新通道投毒https://mp.weixin.qq.com/s/nl_Pntpoys9NVBp7RKLJvg1.目前已知的非法跳转有播放器引用链接：union.maccms.la/html/prestrain.html播放器引用链接：union.maccms.la/html/loading.htmlapplication/extra/active.php文件active.php文件里面有gzuncompress加密，借助ai代码审计后解密后的内容 AI代码审计主要恶意文件：application/extra/active.php 该文件包含大量恶意代码，通过 gzuncompress 压缩隐藏： 解密后发现的内容： 恶意JavaScript脚本：加载外部恶意URL https://code.jquecy.com/jquery.min-3.6.8.js（伪装成jQuery） 恶意域名列表：用于流量劫持和跳转 http://maccms.info:88 https://www.ywquyq.com:10443 https://vodplay2.com 等多个恶意域名 攻击特点： ✅ 仅针对移动端用户（!$env->isMobile() 时跳过） ✅ 绕过管理员IP检测 ✅ 通过 view_filter 钩子自动执行 ✅ 包含反调试功能伪装的jquery链接，在电脑打开，返回内容为空白，电脑切换为手机的ua，才会正常加载内容2.处理方法2.1.屏蔽官方更新更新文件路径：application/admin/controller/Update.php更新文件路径：application/admin/controller/Safety.php将所有 "aHR0cDovL3VwZGF0ZS5tYWNjbXMubGEv" 内容替换为 ""aHR0cDovL3VwZGF0ZS5tYWNjbXMubGEv内容base64解码后为http://update.maccms.la/Update.php文件中，方法内容注释<?php namespace app\admin\controller; use think\Db; use app\common\util\PclZip; class Update extends Base { var $_url; var $_save_path; public function __construct() { parent::__construct(); //header('X-Accel-Buffering: no'); //aHR0cDovL3VwZGF0ZS5tYWNjbXMubGEv $this->_url = base64_decode("")."v10/"; $this->_save_path = './application/data/update/'; } public function index() { return $this->fetch('admin@test/index'); } public function step1($file='') { // if(empty($file)){ // return $this->error(la…

苹果cms10 列表和搜索过滤会员组没有权限的数据
type: item
date: 2026-03-13T14:21:00+00:00
description: AI摘要 苹果cms10在列表和搜索中可能显示会员组无权限数据。新版本可通过后台“网站基础参数-性能优化-数据权限过滤”开启此功能。老版本需手动修改`application/common/model/Vod.php`文件。在`listData`方法中，增加代码以根据当前用户组的`group_type`过滤`type_id`，确保用户只能看到其会员组有权限的内容，避免点击后才提示登录。 此内容根据文章生成，仅用于文章内容的解释与总结 苹果cms10 列表和搜索过滤会员组没有权限的数据苹果cms10的列表中，会出现会员组没有权限的数据，只有点进去才会提示登录。1.新版本后台-》网站基础参数-》性能优化-》数据权限过滤开启即可2.老版本编辑文件application/common/model/Vod.php找到listData方法，增加过滤方法public function listData($where,$order,$page=1,$limit=20,$start=0,$field='*',$addition=1,$totalshow=1) { $page = $page > 0 ? (int)$page : 1; $limit = $limit ? (int)$limit : 20; $start = $start ? (int)$start : 0; if(!is_array($where)){ $where = json_decode($where,true); } $where2=''; if(!empty($where['_string'])){ $where2 = $where['_string']; unset($where['_string']); } // =====修改开始===== // 增加会员组过滤方法 $where3 = []; // 如果是用户请求 if(isset($GLOBALS['user'])){ $category_ids = array_filter(explode(',',$GLOBALS['user']['group']['group_type'])); $where3['type_id'] = ['in',$category_ids]; } $limit_str = ($limit * ($page-1) + $start) .",".$limit; if($totalshow==1) { // 增加where3查询 $total = $this->where($where)->where($where2)->where($where3)->count(); } // 增加where3查询 $list = Db::name('Vod')->field($field)->where($where)->where($where2)->where($where3)->order($order)->limit($limit_str)->select(); // =====修改结束===== //分类 $type_list = model('Type')->getCache('type_list'); //用户组 $group_list = model('Group')->getCache('group_list'); foreach($list as $k=>$v){ if($addition==1){ if(!empty($v['type_id'])) { $list[$k]['type'] = $type_list[$v['type_id']]; $list[$k]['type_1'] = $type_list[$list[$k]['type']['type_pid']]; } if(!empty($v['group_id'])) { $list[$k]['group'] = $group_list[$v['group_id']]; } } } return ['code'=>1,'msg'=>lang('data_list'),'page'=>$page,'pagecount'=>ceil($total/$limit),'limit'=>$limit,'total'=>$total,'list'=>$list]; }

PHP多级分类数据列表转为树型父子结构
type: item
date: 2024-01-10T01:36:00+00:00
description: AI摘要 该PHP函数`formatToTree`用于将MySQL查询出的多级分类列表数据转换为树型父子结构。它通过递归实现，接收数据数组、最上级父节点ID和父ID字段名作为参数，将具有相同父ID的项归类到`children`属性中，从而构建出层级清晰的树状结构。 此内容根据文章生成，仅用于文章内容的解释与总结 PHP多级分类数据列表转为树型父子结构function formatToTree($array, $pid = 0, $pidName = 'parent_id') { return array_values(array_map(function ($item) use ($array, $pid) { $item['children'] = formatToTree($array, $item['id']); return $item; },array_filter($array,function ($item) use ($pidName,$pid) { return $item[$pidName] === $pid; }))); }$array为mysql查询出来的列表数据$pid为最上级父节点id$pidName为父id字段

ThinkPHP6 跨域开起后某些接口请求报错跨域
type: item
date: 2023-02-14T16:33:00+00:00
description: AI摘要 ThinkPHP6部署后部分接口出现跨域报错，经排查发现是路由与控制器命名不规范所致。通过将驼峰式命名改为简洁的单个单词命名，可有效解决该跨域问题。建议开发时保持路由与控制器命名规范统一。 此内容根据文章生成，仅用于文章内容的解释与总结 ThinkPHP6 跨域开起后某些接口请求报错跨域所有接口在本地调试正常，但是部署在服务器之后有的接口出现跨域报错。解决办法路由文件和控制器命名要规范，最好使用单个单词 原# 控制器名 PhotoAlbums.pnp # 控制器类名 PhotoAlbums # 路由名 Route::any('photoAlbums', 'home/photoAlbums/index')->name('photoAlbumsHome');现# 控制器名 Photo.pnp # 控制器类名 Photo # 路由名 Route::any('photo', 'home/photo/index')->name('photoHome');成功解决问题

ThinkPHP6 开起跨域
type: item
date: 2023-02-14T16:30:00+00:00
description: AI摘要 在ThinkPHP6中开启跨域，可通过继承`think\middleware\AllowCrossDomain`新建自定义中间件，并在类中配置`Access-Control`相关响应头。随后在`app/middleware.php`中注册该中间件，即可实现全局或路由级别的跨域支持。 此内容根据文章生成，仅用于文章内容的解释与总结 ThinkPHP6 开启接口跨域1.新建中间件<?php namespace app\home\middleware; use Closure; use think\middleware\AllowCrossDomain; use think\Request; use think\Response; use think\response\Redirect; class Allow extends AllowCrossDomain { protected $header = [ 'Access-Control-Allow-Credentials' => 'true', 'Access-Control-Max-Age' => 1800, 'Access-Control-Allow-Methods' => 'GET, POST, PATCH, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers' => 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With, jwtToken', ]; }2.全局使用中间件或者路由使用中间件app/middleware.php<?php // 全局中间件定义文件 return [ // 加上这句 \app\home\middleware\Allow::class, ];

ThinkPHP6 使用JwtToken
type: item
date: 2023-02-11T09:16:00+00:00
description: AI摘要 本文介绍了在ThinkPHP6中集成JWT Token的方法。通过安装firebase/php-jwt库，创建Token类实现签发与验证逻辑，并配合中间件对请求进行拦截校验，从而实现用户身份的有效管理与安全认证。 此内容根据文章生成，仅用于文章内容的解释与总结 ThinkPHP6 使用JwtToken1.安装JwtTokencomposer require firebase/php-jwt2.新建PHP类文件这里新建了Token.php类文件，用于token的签发和验证<?php namespace app\home; use Firebase\JWT\JWT; use Firebase\JWT\Key; class Token { protected $salt; protected $keyId; public function __construct() { $this->salt = md5("1585364631"); } function getToken($uid): string { $currentTime = time(); return JWT::encode([ "iss" => 'dao', "aud" => '', "iat" => $currentTime, "nbf" => $currentTime, "exp" => $currentTime + 3600 * 24, "data" => [ 'uid' => $uid, ] ], $this->salt, "HS256"); } public function checkToken($token): array { $res = array("code"=>201); try { JWT::$leeway = 60; $decoded = JWT::decode($token, new Key($this->salt, 'HS256')); return [ "code"=>200, "data"=>((array)$decoded)['data'] ]; } catch(\Firebase\JWT\SignatureInvalidException $e) { $res['msg']="签名不正确"; }catch(\Firebase\JWT\BeforeValidException|\Firebase\JWT\ExpiredException $e) { $res['msg']="token失效"; } catch(\Exception $e) { $res['msg']="token无效"; } return $res; } }3.新建中间件文件需要登入使用的进行校验<?php namespace app\home\middleware; use app\home\Token; use Closure; use think\Request; use think\Response; use think\response\Redirect; class UserToken { /** * 处理请求 * * @param Request $request * @param Closure $next * @return Response|Redirect */ public function handle(Request $request, Closure $next) { $token = $request->header('jwtToken',""); $res = (new Token())->checkToken($token); if ($res['code'] != 200 ){ return json($res); } $request->uid = $res['data']->uid; return $next($request); } }

PHP pdo连接mysql数据库查询中文字符为？记录解决
type: item
date: 2023-02-10T07:22:57+00:00
description: AI摘要 PHP使用PDO连接MySQL时，若查询结果出现中文乱码（显示为“？”），可通过在PDO连接参数中添加`PDO::MYSQL_ATTR_INIT_COMMAND => "set names utf8"`来解决，确保数据库连接初始化时正确设置字符编码。 此内容根据文章生成，仅用于文章内容的解释与总结 PHP pdo连接mysql数据库查询中文字符为？记录解决php中，使用pdo连接mysql数据库，查询语句输出结果，中文字符为？解决方法在新建pdo连接中，数组加入对应参数PDO::MYSQL_ATTR_INIT_COMMAND => "set names utf8"例如：new \PDO("$this->dbms:host=$this->host;dbname=$this->dbName", $this->user, $this->pass,[\PDO::ATTR_PERSISTENT => true,\PDO::MYSQL_ATTR_INIT_COMMAND => "set names utf8"]);

PHP 微信公众号对接学习记录
type: item
date: 2023-02-09T06:41:21+00:00
description: AI摘要 本文记录了使用PHP对接微信公众平台的开发过程，涵盖了服务器通信验证、access_token的获取与缓存管理、接收用户消息（XML解析）以及被动回复消息（包括文本及超链接）的实现方法，为开发者提供了基础的对接框架与代码示例。 此内容根据文章生成，仅用于文章内容的解释与总结 PHP开发微信公众平台0.微信公众平台官方文档https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.htmlhttps://res.wx.qq.com/op_res/-serEQ6xSDVIjfoOHcX78T1JAYX-pM_fghzfiNYoD8uHVd3fOeC0PC_pvlg4-kmP1.配置与微信服务器的通信1.打开微信公众平台，获取并写入基本信息index.php文件<?php // 微信公众平台基本信息 $token = 'xxxx'; $AppID = 'wx64xxxxxxxxxf014'; $AppSecret = 'd9bxxxxxxxxxxxxxxxxxxxc2'; ?>2.验证消息是否来自微信服务器index.php文件<?php // 微信公众平台基本信息 $token = 'xxxx'; $AppID = 'wx64xxxxxxxxxf014'; $AppSecret = 'd9bxxxxxxxxxxxxxxxxxxxc2'; // 验证消息是否来自微信服务器 // 计算signature微信加密签名，并且与微信请求字段的signature进行值对比，从而证明消息来自微信服务器 if(!empty($_GET['echostr'])){ // 微信通信请求字段 $signature = $_GET["signature"]; $timestamp = $_GET["timestamp"]; $nonce = $_GET["nonce"]; $echostr = $_GET['echostr']; // 将token，timestamp，nonce三个字段合为一个数组，并且按照字典排序 $arr = array($token, $timestamp, $nonce); sort($arr,SORT_STRING); // 将数组内容拼接为一个字符串 $str = implode($arr); // 将拼接后的字符串内容进行sha1加密 $sign = sha1($str); // 与微信服务器请求的signature字段内容进行对比，则证明是来自微信服务器的消息 if($sign == $signature){ //将echoStr字段的值返回给微信 echo $echostr; }else{ // 内容不一样则退出 return false; } } ?>3.配置微信公众平台1.登入微信公众平台2.点击基本配置3.在“IP白名单” 点击查看，添加服务器IP地址4.在“服务器配置” 点击修改配置URL：网页地址Token：与php文件中的token一样EncodingAESKey：随机生成一个消息加解密方式：兼容模式5.点击提交-确定6.启用2.获取access_tokenaccess_token有效期为两小时，所以1.5小时就重新刷新获取一次access_tokenaccess_token每天请求上线为2000次，每次请求都获取一次access_token，可以将当前access_token缓存在本地文件1.获取access_token架构：本地没有保存access_token的文件，获取access_token，并且保存在文件本地有保存access_token点文件：判断access_token是否过期：过期了，重新获取access_token，保存替换文件没有过期，直接使用2.创建get_access_token.php文件get_access_token.php文件<?php // 文件名 $file = "access_token.dao"; // access_token获取地址，修改appid和secret $get_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx6xxxxxxxxx014&secret=d9bxxxxxxxxxxxxxxxxxx76c2"; // access_token $access_token = ""; function get_access_token(){ // 引用全局变量 global $file,$get_url,$access_token…

PHP生成随机验证码
type: item
date: 2022-05-10T01:46:21+00:00
description: AI摘要 本文介绍了使用PHP生成随机验证码的方法。通过自定义函数，利用GD库创建画布，生成包含字母与数字的随机字符串，并添加干扰像素、线条及边框以增强安全性。最终将验证码存入Session并以PNG图片格式输出。 此内容根据文章生成，仅用于文章内容的解释与总结 PHP生成随机验证码源代码：<?php // $img_w 图片宽 // $img_h 图片高 // $char_len 验证码字符数 // $font 字体大小(内置字体1-5) // $px 随机干扰像素点 // $line 随机线条数 function getCode($img_w=100,$img_h=40,$char_len=5,$font=5,$px=80,$line=10){ //生成码值数组,不需要0，避免与字母o冲突 $char = array_merge(range("A","Z"),range("a","z"),range("1","9")); //随机获取$char_len个码值的键 $rand_keys = array_rand($char,$char_len); //判断当码值长度为1时，将其放入数组中 if($char_len == 1){ $rand_keys = array($rand_keys); } //打乱随机获取的码值键的数组 shuffle($rand_keys); //根据键获取对应的码值，并拼接成字符串 $code=''; foreach($rand_keys as $i){ $code = $code . $char[$i]; } //----1 生成画布 $img = imageCreateTrueColor($img_w,$img_h); //设置背景 $bg_color = imageColorAllocate($img,0xcc,0xcc,0xcc); imageFill($img,0,0,$bg_color); //干扰像素 for($i=0;$i<=$px;$i++){ $color = imageColorAllocate($img,mt_rand(0,255),mt_rand(0,255),mt_rand(0,255)); imageSetPixel($img,mt_rand(0,$img_w),mt_rand(0,$img_h),$color); } for($i=0;$i<=$line;$i++){ $color = imageColorAllocate($img,mt_rand(0,255),mt_rand(0,255),mt_rand(0,255)); imageline($img,mt_rand(0,$img_w),mt_rand(0,$img_h),mt_rand(0,$img_w),mt_rand(0,$img_h),$color); } //矩形边框 $rect_color = imageColorAllocate($img,0x90,0x90,0x90); imageRectangle($img,0,0,$img_w-1,$img_h-1,$rect_color); //----2 操作画布 //设定字符串颜色 $str_color = imageColorAllocate($img,mt_rand(0,100),mt_rand(0,100),mt_rand(0,100)); //设定字符串位置 $font_w = imageFontWidth($font); $font_h = imageFontHeight($font); $str_w = $font_w * $char_len; imageString($img,$font,($img_w-$str_w)/2,($img_h-$font_h)/2,$code,$str_color); $resulf = array( "code" => $code, "img" => $img ); return $resulf; } // $res['code'] 验证码 // $res['img'] 图片 $res = getCode(); session_start(); $_SESSION['captcha_code'] = $res['code']; header("Content-Type: image/png"); imagepng($res['img']); imagedestroy($res['img']); ?>