安全(P0) - 发布包签名:新增 scripts/package_keygen.php、scripts/package_verify.php、 ywxapp\service\PackageVerifier;FrameworkService::apply() 解压前强制验签 (ywxapp.framework_package_verify,默认开启) - JWT 收紧:config/jwt.php 移除公开默认密钥,JwtService::assertStrongSecret() 校验非空 / 长度≥32 / 已知弱值;.env.production.example 键名由 JWT_SECRET_KEY 修正为 JWT_SECRET - /api/v1/Article:免登录白名单收窄为只读接口,写操作字段白名单 + assertOwner() 越权校验 - 开发者令牌:查找接口不再回显明文、统一失败文案 + 限流 - TemplateInstaller:文件名白名单 + realpath 归一化 + 目录内断言,修复 Zip Slip;补 use ZipArchive - 后台登录:接入验证码、失败锁定与登录限流(config/ratelimit.php) - 缓存 / Session 驱动可由 .env 覆盖(CACHE_DRIVER / SESSION_TYPE),集群部署改 redis - paycenter 支付日志开关改为 PAY_LOG,不再跟随 APP_DEBUG 工程化 / 交付 - 新增 CHANGELOG.md 与 .env.production.example;composer.lock 入库锁定依赖版本 - 插件离线安装 CLI 增加 --uid,后台上传安装透传 uid,付费插件授权校验可通过 - 授权拒绝文案区分「未携带 uid」与「未找到有效授权」,便于排障 - .gitignore 放行 .env.production.example / .env.addon.example / .gitattributes / public/.htaccess 结构 - appmall 由 addon/appmall 迁移为独立应用 app/appmall(市场 / 开发者中心 / 审核 / 分润)
5.2 KiB
5.2 KiB
更新日志(CHANGELOG)
本项目版本号采用语义化版本(主版本.次版本.修订),唯一事实源是 config/ywxapp.php 的 version,
由 scripts/package_framework.php 递增并写回。发布流程见 docs/打包与发布指南.md 的「发版 Checklist」。
[1.1.2] — 待发布(Unreleased)
一次以「可对外发布」为目标的安全与工程化收敛,未引入新业务功能。
安全修复(Security)
- SEC-1 主框架升级包无来源认证:官网 / CDN / DNS 被攻破即可向全部客户机推送任意代码。
新增发布包签名体系
ywxapp\service\PackageVerifier(RSA-SHA256 签名 + 内容清单哈希 + 防降级),FrameworkService::apply()解压前强制验签,未部署公钥时 fail-closed; 打包脚本自动签名,配套scripts/package_keygen.php与scripts/package_verify.php。 - SEC-2 JWT 默认密钥可用:
.env.production.example键名误写作JWT_SECRET_KEY(代码读JWT_SECRET), 照模板部署等于用公开密钥签发 token。模板键名已修正,config/jwt.php默认密钥改为空,JwtService签发前强制校验强度(不少于 32 字节且不得命中弱值黑名单)。 - SEC-3
/api/*文章写接口未授权:$noNeedLogin=['*'],且$needRight字段ApiBase根本不认, 任何人可增删改全站文章。白名单收窄为只读的index/detail/init, 写操作需登录且只能操作自己的文章(新增assertOwner()),create 改为字段白名单且归属uid由服务端写入。 - SEC-4 后台登录防护缺失:验证码参数被静默丢弃、账号锁定逻辑从未被调用、后台无限流。
AdminAuth::login()补齐验证码校验、账号锁定isLocked()、IP 与账号双维度限流封禁(配置项login_fail_*)。 - SEC-5 开发者令牌明文回显且邮箱可枚举:POST 任意邮箱即返回 token(可用于发布插件、改价、提现)。 改为只发邮件不回显、统一响应文案防枚举、随机延时防时序侧信道、IP 与邮箱限流。
- SEC-6 模板包 Zip Slip:
templates/<name>/前缀匹配后直接拼接路径,可写入任意目录。 新增模板名白名单、相对路径..拦截、落盘前realpath越界校验三道防护。
变更(Changed)
config/ratelimit.php的 RateLimit 中间件仍只注册于 api 应用,后台登录改由AdminAuth自带计数。.env.production.example补齐JWT_SECRET、安全开关、缓存与 Session 驱动、市场角色。config/cache.php的default、config/session.php的type支持CACHE_DRIVER/SESSION_TYPE(多副本部署切 redis)。config/ywxapp.php新增:framework_package_verify、package_public_key_path、login_fail_limit/window/block_time;addon_license_check支持ADDON_LICENSE_CHECK。composer.json:yansongda/pay、hyperf/pimple由通配*锁定到^3.7.20/^2.2.2;platform-check由 false 改 true;composer.lock不再被忽略(可复现构建)。- 删除
.travis.yml(ThinkPHP 官方脚手架残留,含他人加密 deploy key)。
升级须知(Breaking)
- 升级包必须签名:1.1.2 起的客户机默认要求发布包带
signature.json。 自建升级服务器的内网场景可临时用FRAMEWORK_PACKAGE_VERIFY=false降级(不推荐)。 - 未配置
JWT_SECRET将无法签发任何 token(刻意的 fail-closed)。 升级前请执行openssl rand -base64 64并写入.env。 /api/v1/article的 create / update / delete 需登录。 此前依赖匿名写库的下游客户端需改为携带 token。- 开发者令牌找回页不再回显令牌,改为邮件发送;
前端
app/appmall/view/developer/mine.html已兼容(无 token 时不展示令牌块)。
已知限制与后续(Known Issues)
本节记录本次未处理、需要在后续版本跟进的问题,避免它们被误当成「已修复」:
- 数据库自愈无版本号、无法回滚:约 60 处模型依赖
BaseModel.php从public/install/install.sql补表, 且该路径写死在代码里 —— 按安全要求删掉 install 目录后自愈会静默失效;多副本冷启动还存在并发建表竞态。 计划改为「升级 SQL 清单 + 已执行版本表」,自愈降级为兜底。 - 插件包(addon)尚未接入发布包签名体系:本次只加固了主框架升级链路。
插件在线安装有
addon_secret下载签名可选调用,但没做包内完整性校验,下一步复用PackageVerifier。 - 无 CI / 单元测试 / 静态分析:本次修复的自检均为临时脚本(已删除)。
建议补
tests/+ GitHub Actions,至少覆盖支付回调签名、Zip 安装路径安全、JWT 密钥策略三条主线。 catch (\Throwable)静默吞异常:全仓约 144 处(尤以BackendSchemaMiddleware、BaseModel居多), 故障排查成本高,建议逐步替换为「记录 + 上报」。app/api/controller/v1的 paginate 分页数偏大(如 1000),缺少服务端上限,建议统一由paginate()默认限制。