尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

贷款超市源码安全审计:ThinkPHP部署、PHP版本兼容与漏洞排查指南

贷款超市源码安全审计:ThinkPHP部署、PHP版本兼容与漏洞排查指南 简介金融贷款口子超市V2源码是一套基于ThinkPHP框架开发的金融贷款产品推广与管理平台面向有意搭建贷款口子超市的运营者、代理及金融科技创业者。系统支持贷款产品一键推广、三级分销佣金结算并内置数据分析和安全控制模块可帮助平台方高效掌握销售动态、降低运营风险。资源包共2000个文件约72.6MB核心包括552个PHP后端逻辑文件、419个PNG和378个GIF图片资源、207个JS与156个HTML前端页面以及60个CSS样式和SQL数据库文件并附带安装视频教程帮助新手完成从环境配置到上线的完整部署。目前已有275人学习下载。通过这套源码用户能直接获得可复用的金融贷款推广系统、分销机制构建思路以及前端后端一体化的项目结构与安全实践参考适合作为快速入局金融贷款垂直领域的起步模板。1. 一套“贷款超市V2”源码到手上先分清它能干什么“贷款口子超市V2”这个标题里的“口子”在信贷行业泛指各类非银放款渠道“超市”则是把多个渠道集中展示再叠加积分兑换和小商城于是形成一套“借款导流实物商品兑换”的混合型平台。拿到这套 ThinkPHP 源码时先别急着安装——我一般会先确认三件事入口文件是否被加密或混淆过数据库脚本里是不是带着测试数据后台菜单里有没有藏计划任务、定时回调或自动扣款逻辑。贷款超市的业务复杂度并不高却把会员、权限、订单、积分、渠道接口这些常见模块全部揉在一起适合做 ThinkPHP 应用审计的练手样本对不熟悉框架的人而言真正的坑往往出在 PHP 版本兼容和目录权限而不是业务代码本身。需要先说明一点贷款导流、砍头息、非持牌资金撮合在国内属于强监管甚至违规业务。这篇文章只讨论“拿到一套源码后如何做环境部署、代码走读、安全审计和验收验证”不讨论任何运营方法也不提供任何绕过合规约束的技巧。2. 识别 ThinkPHP 版本与 PHP 兼容性本地跑通最小部署2.1 先看入口文件和应用目录结构判断是 3.2 还是 5.xThinkPHP 项目的目录结构能直接暴露大版本。打开根目录后我一般先看 3 个标记是否存在Application目录、是否存在ThinkPHP框架目录、入口文件index.php里的加载路径写法。常见的对应关系是TP3.2 的入口文件加载ThinkPHP/ThinkPHP.php应用目录叫ApplicationTP5 及以上版本入口文件通过require引入public/index.php或think启动文件应用目录叫app。# 在源码根目录下执行快速确认框架版本特征 ls -la | head -30 cat index.php 2/dev/null | head -40 find . -maxdepth 2 -name ThinkPHP.php -o -maxdepth 2 -name base.php 2/dev/null这段命令的逻辑是ThinkPHP.php存在时大概率是 TP3.2base.phpApp.php则偏向 TP5/TP6。find限制了最大深度避免把Runtime缓存目录里的文件也扫进来。如果看到Application/Common/Conf/config.php说明是 TP3.2 的经典布局如果看到config/database.php或.env则是 TP5 之后的写法。2.2 PHP 版本与 ThinkPHP 版本如何匹配兼容性核心在 php8这是部署时最容易翻车的一步。贷款超市 V2 这类商业源码老版本多数基于 TP3.2 开发而 TP3.2 原版并不兼容 PHP 8。PHP 8 移除了each()、create_function()等函数并把字符串与 null 比较的行为改了老代码会直接抛Fatal error。本地调试建议优先用 PHP 7.4原因如下表框架版本推荐 PHP 版本重要说明TP3.2 / TP3.2.3PHP 5.4 ~ 7.4大批老代码用I()、M()方法PHP 8 下需要做兼容性补丁TP5.0 / TP5.1PHP 5.6 ~ 7.4可用 PHP 8但个别扩展如php_curl的写法需要验证TP6.0PHP 7.1 ~ 8.x如果源码是 TP6直接用 PHP 8.1/8.2 均无大碍未标识版本先用 PHP 7.4 试跑装完看报错再决定是否降级或打补丁我自己在部署时会建立一个“源码审计专用”的 PHP 7.4 环境而不是直接上 PHP 8因为排查老代码语法错误要快得多。如果你确认源码已被二次开发成兼容 PHP 8 的版本那么index.php里通常能看到define(APP_DEBUG, true)或修改过的高精度函数替代代码。2.3 本地跑通的最小操作步骤用一个集成环境软件比如 phpStudy / 宝塔面板 / Laragon 均可建立站点把源码放到站点根目录然后按下面顺序操作# 1. 在站点根目录创建 runtime 目录并给足写权限 mkdir -p Runtime chmod -R 755 Runtime chmod -R 755 Application 2/dev/null # 2. 把伪静态规则写入 Apache 或 Nginx 配置 # Nginx 下在 server 块中加如下配置 # location / { # if (!-e $request_filename) { # rewrite ^(.*)$ /index.php?s$1 last; # } # }然后访问http://你的站点域名/install或根目录观察是进入安装向导还是直接报错。如果直接显示目录列表或返回 ThinkPHP 模板错误第一件事是查看Runtime/Logs下的日志文件常见错误集中在数据库连接失败、PHP 版本函数不兼容、缺少mcrypt或curl扩展三类。数据库配置一般写在Application/Common/Conf/config.php或.env里确认数据库地址、端口、账号、密码正确后再刷新。装完后台先把Install目录改名或删除这是最基本的操作。3. 贷款超市的模块边界、数据表设计与订单流转代码走读3.1 功能模块划分借款导流、商城兑换、会员代理三线并行贷款超市平台从用户视角看是两层前端面向借款人展示“贷款产品”引导用户填写手机号和申请额度提交后把信息推给渠道后端除了内容管理还通常有一套代理/推广员体系用来追踪每个申请来自哪个推广员。再加“超市”功能后订单系统要把实物商品、积分抵扣、线下发货串起来。我拿到这类源码后会先列出 Controller 目录按路由把模块对应关系摸清。比如Application/Home/Controller/IndexController.class.php处理首页LoanController或ProductController处理借款产品OrderController处理超市订单。还有一类重要的公共控制器是ApiController专门接收渠道回传的放款状态或“送审结果”回调。模块边界明确了后续做越权测试和改造才有抓手。3.2 核心数据表结构贷款超市的关键表不复杂常见设计如下字段名在不同源码里可能有差异但业务含义基本一致-- 贷款产品表 CREATE TABLE loan_product ( id int(11) NOT NULL AUTO_INCREMENT, title varchar(100) NOT NULL COMMENT 产品名称, min_amount decimal(10,2) DEFAULT NULL COMMENT 最低可借金额, max_amount decimal(10,2) DEFAULT NULL COMMENT 最高可借金额, min_rate decimal(5,2) DEFAULT NULL COMMENT 最低利率/日息, channel_id int(11) NOT NULL COMMENT 渠道ID, status tinyint(1) DEFAULT 1 COMMENT 1上架 0下架, sort int(11) DEFAULT 0, create_time int(11) DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT借款产品表; -- 借款申请记录表 CREATE TABLE loan_apply ( id int(11) NOT NULL AUTO_INCREMENT, user_id int(11) NOT NULL COMMENT 用户ID, product_id int(11) NOT NULL COMMENT 产品ID, real_name varchar(32) DEFAULT NULL COMMENT 申请人姓名, mobile varchar(20) NOT NULL COMMENT 手机号, amount decimal(10,2) DEFAULT NULL COMMENT 申请金额, source varchar(32) DEFAULT h5 COMMENT 来源h5/mini/api, status tinyint(1) DEFAULT 0 COMMENT 0待送审 1已推送 2放款成功 3拒绝, callback_time int(11) DEFAULT NULL COMMENT 渠道回调时间, PRIMARY KEY (id), KEY idx_mobile (mobile) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT借款申请记录;上述结构里loan_product.channel_id是渠道维度表示同一个产品在不同渠道下的差异化配置loan_apply.status是生命周期的核心字段从待送审到已推送再到放款成功整个过程通过渠道回调来驱动。实际源码中可能还有agent_id或promo_code用来锁定推广员这个字段必须在用户授权的前提下生成不能从 URL 参数里随手拼接。3.3 借款申请与渠道回调用到的关键参数下面这段是 ThinkPHP 3.2 时代典型的借款申请提交逻辑我重新整理其参数含义public function submit_apply() { $user_id intval($_SESSION[user_id]); $product_id intval(I(post.product_id)); $amount floatval(I(post.amount)); $mobile trim(I(post.mobile)); // 防重复提交同一用户同一产品在 5 分钟内不能重复申请 $lockKey loan_apply_ . $user_id . _ . $product_id; if (S($lockKey)) $this-error(申请处理中请勿重复提交); S($lockKey, 1, 300); $product M(loan_product)-where(array(id $product_id, status 1))-find(); if (!$product) $this-error(产品不存在或已下架); $apply_data array( user_id $user_id, product_id $product_id, mobile $mobile, amount $amount, source h5, status 0, create_time time() ); $apply_id M(loan_apply)-add($apply_data); // 推送到渠道 $this-push_to_channel($apply_id, $product_id, $mobile, $amount); }其中S($lockKey)是 ThinkPHP 缓存锁用来做 5 分钟防抖I(post.product_id)是框架自带的过滤函数但仍然要自己对amount做浮点数强转否则可能把负数或超长字符串写入数据库。push_to_channel里通常会拼装一个回调地址例如回调地址 域名/Api/callback?order_snxxx渠道拿到订单号后回传状态。审计这套代码时重点看回调地址是否做了签名校验以及order_sn是否用时间戳加随机数生成避免被枚举遍历。常见的问题是渠道回调地址直接用明文外呼接口没有sign参数这属于高危链路上线前必须补上。4. 上线前的安全加固注入、越权、敏感数据泄露逐个查4.1 用 grep 快速排查 SQL 注入与参数拼接贷款超市源码里最容易出现 SQL 注入的是搜索、排序和筛选功能。先别急着读全部代码用一组 grep 命令锁定可疑位置# 在 Application 目录下搜索直接拼接 SQL 的写法和危险函数 grep -rn where.*\$_ Application/ 2/dev/null | head -30 grep -rn M(.*)-where.*\\.\$ Application/ 2/dev/null | head -30 grep -rn query(\|execute( Application/ 2/dev/null | head -30query()和execute()是 ThinkPHP 中直接执行原生 SQL 的方法如果参数来自用户输入且未做预处理就有注入风险。兼容写法的修复思路是改成参数绑定// 不安全的写法示意 $list M(loan_product)-where(channel_id . $_GET[cid])-select(); // 安全写法使用数组条件框架自动做参数过滤 $list M(loan_product)-where(array(channel_id intval(I(get.cid))))-select();代码逻辑说明数组方式会走框架自身的绑定机制配合intval强制把参数转成整数能直接切断数字型注入如果是字符串条件则必须使用exp之外的方式手动bind。审计时不要迷信I()函数的过滤能力它在老版本里主要做的是格式规整不是安全边界。4.2 越权访问检查点查看订单、查看申请记录、提现操作越权是贷款超市中非常普遍的问题尤其是借款申请记录里包含姓名、手机号这类敏感信息。测试时打开两个浏览器账号A 账号拿到订单 ID 后用 B 账号直接访问订单详情接口如果返回了数据就说明存在水平越权。代码层的关键在于取user_id的方式// 错误user_id 从请求参数读取攻击者可以传别人的 ID $user_id intval(I(get.user_id)); $apply_list M(loan_apply)-where(array(user_id $user_id))-select(); // 正确user_id 从登录态获取不接受前端传入 $user_id intval($_SESSION[user_id]); $apply_list M(loan_apply)-where(array(user_id $user_id, status array(gt, 0)))-select();类似检查点不止申请记录商品订单、积分明细、收货地址、优惠券都要逐一过一遍。更隐蔽的越权是“修改自己订单时篡改订单号”这会导致把别人的收货地址改成自己的这类逻辑适合用自动化脚本跑参数遍历。对于 TP3.2 的老代码如果控制器里有大量_before_或_initialize()方法未做登陆校验建议统一抽出BaseController在所有需要登录的控制器里强制继承。4.3 敏感数据与日志泄露排查贷款超市这类平台最大的合规隐患不是功能缺失而是数据库字段里存了非必要的用户信息。用下面命令检查日志文件和数据导出文件# 查找不应对外访问的目录 find . -type d -name Runtime -o -type d -name *.sql -o -type d -name log 2/dev/null # 查看 git 历史是否有配置文件泄露 find . -name *.sql -o -name *.bak -o -name *.swp 2/dev/null | head -20发现Runtime可以直接被浏览器访问时应把 Web 服务器根目录指向public或限制Runtime目录的访问权限。数据库配置文件里的密码如果是明文也要在部署后更换数据库账号密码并将配置文件排除在备份包之外。另外注意申请记录表里的mobile字段应该是脱敏后才返回前端如果源码中直接把完整手机号输出到列表页需要改成substr_replace形式打码后再展示。5. 用一份验收清单验证源码完整性和升级闭环拿到所谓“亲测源码”最怕的是安装到一半发现数据库脚本缺失、后台缺文件或者源码包里有“后门”文件。我会按下面两条链路快速验收后再碰业务。5.1 安装和登录链路检查第一链路是全新安装新建空数据库导入 SQL 文件修改配置访问首页确认能正常展示产品列表访问/Admin或/admin.php进入后台使用默认账号登录看是否有验证码校验。第二链路是业务流程前端注册一个测试账号提交一笔借款申请到后台查看这条申请的状态变化再走一条超市购物流程看积分抵扣、订单生成、后台发货三个环节是否贯通。登录后台后把“会员列表-导出”功能点开确认导出文件里是否包含手机号明文这一步直接决定后续合规改造成本。5.2 备份恢复和变更验证# 备份数据库 mysqldump -u root -p loan_supermarket backup_$(date %Y%m%d).sql # 恢复数据库 mysql -u root -p loan_supermarket backup_$(date %Y%m%d).sql # 对比关键表行数 mysql -u root -p -e select count(*) from loan_apply; select count(*) from shop_order;恢复后跑一遍登录、申请、商品提交确认关键表的主键没有重置、关联数据没有断掉。对这套代码做二次开发时我强烈建议把原版文件先打一个 tar 包存到仓库外再在 Git 里建立v2-rebuild分支否则后期改动一多你根本分不清哪些是原始逻辑、哪些是加固代码。loan_apply.status这类状态机字段改动前先确认渠道回调是否以最终态覆盖避免渠道异步通知比主动查询先到时把状态“倒推”回去。最后留在手里的是一条验证命令和一张字段清单把路由表、数据库字段、回调签名、越权测试结果整理成表下次再拿到类似的 ThinkPHP 贷款平台源码你只需要复制这套审计路径。本文还有配套的精品资源点击获取
返回列表