Files
YwxAppThink/CHANGELOG.md
T
yangwx 18101cf11a feat: 上线前安全加固与发布工程化;appmall 由插件迁移为独立应用
安全(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(市场 / 开发者中心 / 审核 / 分润)
2026-10-22 00:53:39 +08:00

5.2 KiB
Raw Blame History

更新日志(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.1.2 起的客户机默认要求发布包带 signature.json。 自建升级服务器的内网场景可临时用 FRAMEWORK_PACKAGE_VERIFY=false 降级(不推荐)。
  2. 未配置 JWT_SECRET 将无法签发任何 token(刻意的 fail-closed)。 升级前请执行 openssl rand -base64 64 并写入 .env。
  3. /api/v1/article 的 create / update / delete 需登录。 此前依赖匿名写库的下游客户端需改为携带 token。
  4. 开发者令牌找回页不再回显令牌,改为邮件发送; 前端 app/appmall/view/developer/mine.html 已兼容(无 token 时不展示令牌块)。

已知限制与后续(Known Issues)

本节记录本次未处理、需要在后续版本跟进的问题,避免它们被误当成「已修复」:

  1. 数据库自愈无版本号、无法回滚:约 60 处模型依赖 BaseModel.php 从 public/install/install.sql 补表, 且该路径写死在代码里 —— 按安全要求删掉 install 目录后自愈会静默失效;多副本冷启动还存在并发建表竞态。 计划改为「升级 SQL 清单 + 已执行版本表」,自愈降级为兜底。
  2. 插件包(addon)尚未接入发布包签名体系:本次只加固了主框架升级链路。 插件在线安装有 addon_secret 下载签名可选调用,但没做包内完整性校验,下一步复用 PackageVerifier。
  3. 无 CI / 单元测试 / 静态分析:本次修复的自检均为临时脚本(已删除)。 建议补 tests/ + GitHub Actions,至少覆盖支付回调签名、Zip 安装路径安全、JWT 密钥策略三条主线。
  4. catch (\Throwable) 静默吞异常:全仓约 144 处(尤以 BackendSchemaMiddleware、BaseModel 居多), 故障排查成本高,建议逐步替换为「记录 + 上报」。
  5. app/api/controller/v1 的 paginate 分页数偏大(如 1000),缺少服务端上限,建议统一由 paginate() 默认限制。