[公告] 明天或后天会发布一个小版本:FlintHub1.0.20

👑Lv.11 元老 🌏 正式会员
2026-09-18 17:09:38

到时候会发布完整增量包和完整安装包,请及时更新。

update/20 改动说明(精简版)

版本:V1.0.20 · 状态:D 批进行中,尚未冻结 覆盖:95 个代码文件 + 改动说明.md,共 96 个文件 验收:765 条断言 / 0 失败 + HTTP 冒烟 28/0 + 镜像 23/23 MATCH


一、批次总览

批次 主题 项数
A 安全止血 8
B 机制修复 + 数据一致性 14
C 契约断裂专项 9 + 全项目扫描
D 批量硬化(D1~D9) 约 60

二、A 批:安全止血(8 项)

编号 问题 改法
S-1 净化器对 <style>/<script> 的 CDATA 节点完全失效 → 存储型 XSS 白名单循环前整块 removeChild 6 类 raw-text 元素 + 输出侧 CDATA 节点走 htmlspecialchars()
H-4 搜索 type 无白名单 → 反射型 XSS(分页 URL 未转义) pagination() 三出口统一转义(含 $prevHref/$nextHref)+ $type 白名单
H-3 /api/upload 省略 category_id 即绕过 can_attach 抽出 canUploadAttachment() 默认拒绝;显式版块须真实存在 + 权限判定;/api/fetch-image 复用同判定
H-2 users.status 全链路不校验 → 封禁完全失效 Auth::getCurrentUser() / RememberMe::check() / AuthController::login() 三处统一判 status !== 'active'
V-M10 ?msg[]=x 数组参数触发 TypeError → 500 index.php 入口剥离 $_GET 非标量项;TemplateCompiler::query() 作为视图唯一入口
M-1 APCu 未启用 → 每请求刷错误日志(178 条) 新增 apcuUsable() 真实探测;flush() 拆 APCu 只读段与 DB 写入段
A7 默认密钥硬编码并随包分发 支持环境变量 / protected/*_key.php 覆盖 + *_IS_DEFAULT 标记;后台告警条
A8 幽灵路由 /admin/database/convert 必现 500 删除两条路由

不做:MAIL_PASS_KEY / CRON_KEY 轮换(config 本地不一致,会解不开存量 SMTP 密文)


三、B 批:机制修复(14 项)

编号 问题 改法
H-5 计数器系统性漂移(实测 70 vs 72) deleteWithCascade 补状态过滤 + Post::softDelete 下沉维护 reply_count + Settings::runtimeGet 兜底改只读
H-1 跨库写路径零事务(extern / 桶 / 索引三步非原子) 统一写入顺序 + 每步失败即时补偿(@unlink + DELETE),把静默分裂降级为可观测失败
H-8 外置正文存储锁不一致 + 缓存不失效 ExternMerger 复用 $idxPath.lock;写 .idx 后统一 apcu_delete
H-6 5 个后台控制器审计零记录 UserController/PluginController/GroupController/TrashedController/CategoryController/DatabaseController(备份下载)共 13 处补 AuditLog::log
H-7 插件 system:settings 权限可绕过 Setting::setValue() 下沉校验 + debug_backtrace 反查插件来源 + 补齐 5 处漏传
M-2 RateLimiter 检查-写入非原子 + 无锁读 收进同一 LOCK_EX 临界区;json 损坏保守拒绝;cron 清理 7 天陈旧缓存
M-5 sanitizeOrderBy 黑名单 / update() 返回 PDOStatement / insert() 无列白名单 改解析式白名单 + 返回受影响行数 + PRAGMA table_info 列白名单
M-7 Permission 无记录默认放行 → 新版块隐蔽越权 默认拒绝,仅整表为空时放行
M-9 改密后旧会话与「记住我」不失效 ProfileController 改密后 RememberMe::clearTokens()
M-10 前半 点赞可无限刷积分(TOCTOU) 条件写 + rowCount() 判定首次
M-11 归档只读限制仅 show 有惰性标记 抽 lazyMarkArchived() 在 edit 前调用
M-6 回帖事务横跨插件 hook / 通知 / 上传 事务只包业务表写入,钩子/通知/上传移出
M-1 后半 ViewCounter 并发重复落库 apcu_cas 抢占 + COMMIT 后 apcu_delete
低危 CSRF 无时效判定 + csrf[]=x 触发 500 verify() 补时效 + 非标量兜底;config.php 补 display_errors=0

不做:M-10 后半 points_log 唯一索引(会破坏回帖/骰子/邀请码等正常重复业务)


四、C 批:契约断裂专项(9 项 + 1 次扫描)

特征:页面上看着正常,实际保存后回落到未筛选状态、字段被悄悄改写、或整块结果永远空白。

编号 问题 改法
V-M06 权限矩阵保存后回跳丢 category_id → 页面回落空矩阵 回跳携带 category_id
V-M08 插件编辑保存静默丢弃 category 控制器透传
V-M09 自动扩容阈值每次保存被改写为 1 改为「字段存在才写」(视图按设计不渲染该字段)
V-M13 博客封面无法移除(控制器有消费、视图无入口) 补复选框 + 三语键
V-M02 搜索「用户」标签被无条件 continue,永远空白 补用户结果卡片分支
V-M03 搜索高亮入参预先 e() → 双重转义 4 处调用改传原始文本
V-M14/L19 分页丢筛选参数(blog 归档 / search 时间范围) 补参数 + rawurlencode
V-M11 messages 无 username 列,视图键名不对齐 → 收发件人恒为空 模型别名对齐 + 详情页补齐
V-M15 密码不合规时 tokenValid 变 false → 表单被收起 改为 (bool)$row

C0 扫描:scan_form_contract.php → ①「控制器读 $_POST 但无视图渲染」= 0 项(退出码 0);沉淀为发布前检查项。


五、D 批:批量硬化(D1~D9)

D1 BASE_PATH 家族(11 文件)

问题 改法
视图/渲染层直读 REQUEST_URI 未剥离部署前缀 → 子目录部署下布局/导航/侧栏整体错乱 抽 TemplateCompiler::currentPath(),收敛 9 处直读;偏好 cookie path 随 BASE_PATH

D2 <script>/CSS 上下文口径(12 文件)

问题 改法
<script> 内误用 HTML 转义(浏览器先实体解码再 JS 解析)/ json_encode 缺 JSON_HEX_* / CSS 值上下文注入 新增 TemplateCompiler::cssValue();JS 上下文统一 json_encode + JSON_HEX_*

D3 i18n(5 文件)

问题 改法
logs/audit.php 全页硬编码中文;缺 admin.queue_mode_switch_confirm;plugins/xiuxian/lang/ 缺 zh_tw.php 三语各 1318 键(1302→1318)

D4 可用性/不可逆操作(6 文件)

问题 改法
主题设置表单无提交按钮、全靠 JS;数据库危险页无常驻警示、确认全依赖 Alpine <noscript> 兜底提交按钮;危险页常驻警示 + 服务端 CONFIRM 硬拦截

D5 内容完整性(6 文件)

问题 改法
Content::decode() 非幂等 + 视图二次解码 → 正文静默改写 解码职责归位模型;修正 Thread.php 两处错误注释;引用框改用已解码值

D6 缓存(2 文件)

问题 改法
任一用户切私有偏好即清空全站静态页缓存;PageCache 落盘非原子 Theme::set() 加「值真变化才清」;落盘改 tmp + rename

D7 模板引擎(1 文件)

问题 改法
safeExpr() 只检查第一个函数调用 → isset($x) && system("id") 放行 preg_match_all 遍历全部 + 完整标识符比对(当前无可达注入点,属回归封堵)

D8 服务端校验(12 文件,6 新增)

问题 改法
标题/正文长度仅前端 maxlength;LIKE 降级搜索缺 ESCAPE;通知聚合从渲染后文案正则抓数字;建表 DDL 进程内重复执行 新增 Content::lengthError()(7 写入点);LIKE_ESCAPE 常量;notifications.agg_count 列;PRAGMA user_version + 进程内缓存

D9 低危收尾(分组)

组 1:L2 BASE_PATH 守卫 / L16 /resend-verification 补 POST 路由 / L19 CSP 补 3 条 / L29 登录时序侧信道 / L32 json() 脏数据 500 / L33 开放重定向补拒反斜杠 / L17 $categoryId 定义顺序

组 2:L14 分页页码钳制 / L25 标签去重 + 上限 + uk_tags_name 唯一索引 / L15 改邮箱预检

组 3:L3 三处 GET 即写操作 → POST + CSRF(/layout-toggle、通知已读、重建统计)

组 4:L6 归档桶只读(PRAGMA query_only + 11 处维护路径旁路)+ 归档竞态 ③.5 复检 / L7 删除路径容错 + 补偿回滚 / L8 活跃桶缺失不再静默建库

组 5:L10 模型层缓存加 500 上限 + 修 array_shift 键重置缺陷 / L11 busy_timeout 还原 / L21 Settings 对账加值比对 / L24 UserPrefs::clear()

组 6:L5 内置组拒绝改名 / L12 验证码降级盐随机化 + /api/captcha 限流 / L18 站点名去双重转义 / L20 首页前置缓存键对齐 / L22 备份路径 isWithin() + zip 白名单 / L23 Imagick 魔数闸门 / L26 钩子 include 作用域隔离 / L27 主题名白名单 / L31 closeAll() 释放句柄

L9 + L28:残留桶目录备份后清理 + 桶文件集合自检;SessionHandler 实现 validateId / updateTimestamp,use_strict_mode 真正生效


六、不做的事(已决策)

项 决策 理由
MAIL_PASS_KEY / CRON_KEY 轮换 不做 config 本地不一致,会解不开存量 SMTP 密文
M-12 索引优化 不做 36 万数据实测无问题
M-10 后半唯一索引 不做 会破坏回帖/骰子/邀请码正常业务
B2 第 2 步(wal_journal) 本包不做 先上最小补偿,按序分两次发布
L34 sw.js CACHE_VERSION 待确认 递增会强制全站访客重拉静态资源
L12 后半 PoW 难度 待确认 影响低端设备登录耗时
架构调整 不做 按 Q6 只做扫描

七、部署要点

  1. 覆盖 95 个代码文件(含 version.php),包内共 96 文件
  2. config.php 禁止直接覆盖:须手工合并 A7 密钥段 + B7 display_errors 两行(见附)
  3. 清 data/runtime/pages/*.html:D9 组 3 改了导航输出,旧缓存含 href="/layout-toggle?next=…",游客点击会 404
  4. 后台点一次「维护 → 重建统计」对齐 _runtime_total_*
  5. D8 notifications.agg_count 列 / D9 组 2 uk_tags_name 索引:首次访问自动补齐,无需迁移脚本
  6. data/.htaccess 不再由 zip 导入还原:今后用后台导入备份时若缺该文件需手工放置
  7. L9 残留桶清理:本包只含自检代码,生产站如有同类残留须先比对 bucket_path 全集、备份后再清理

八、验收数据

批次 断言数 失败
A 11 项 0
B ~250 0
C 55 + 11 + 11 + 扫描 0
D1~D8 ~400 0
D9 组 1~6 ~400 0
L9 + L28 57 0
合计 765 0
  • 镜像校验:95 文件站点侧 ↔ 包内 SHA-256 全部一致
  • HTTP 冒烟:smoke_http_c1 11/0、smoke_http_g3 17/0
  • 编码:全部 UTF-8 无 BOM + 纯 LF
  • 三语 i18n:各 1326 键、集合完全一致

九、已知限制(诚实披露)

  1. 无 APCu / 无 pcntl:M-1 并发分支、H-8 并发合并、M-2 真并发、B6 并发穿透 —— 本机无法构造真并发,需 Linux + 生产规模副本复验
  2. BASE_PATH 后台设置仍不生效:本批只消除 Warning;真正生效需把常量改运行时变量(架构调整)
  3. CSP 仍含 unsafe-inline / unsafe-eval:移除需改造全站内联脚本(架构级)
  4. 归档竞态仍有毫秒级残留窗口:彻底消除需跨文件事务
  5. Content::decode 注释已纠正为「非幂等」:历史数据可能含 base64 态
  6. updateTimestamp 行不存在时回退 write():兼容 gc 中途清行场景,正常路径仍只一条 UPDATE

最后由 flinthub 于 2026-09-18 22:27 编辑
轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 浏览 162 次 | 回复 9 次

全部回复 (9)

🌳Lv.4 中级 ⭐️ 新访客
2026-09-18 17:21:19
版本更新本质是个依赖图:增量包只打补丁,动的是受影响的那几个子图;全量安装则是整图重建,稳妥但费带宽。小版本1.0.20一般拓扑序没变,局部改动居多。建议先扫一遍changelog,看改动了哪些节点、有没有破坏性接口,再决定走增量还是全量。哈哈,连发版都能扯上图。
#1 楼
💡Lv.10 顾问 🌏 正式会员
2026-09-18 17:45:46
坐等更新
知识,奉行,知行合一
#2 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-18 18:48:44
小版本更新先别急着全量推,增量包丢测试环境跑两天,确认调度和快照策略没变再上生产。升级前必打宿主机快照,别问我为啥知道,上次谁没打快照谁背锅。虚就完事了,哈哈。
#3 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-18 19:07:17
小版本更新这事儿,说白了就跟给车做保养一样——增量包是换机油,轻快省事;完整安装包是直接换台新车,图个干净。FlintHub 1.0.20 这俩都给你备齐了,别嫌麻烦,该升就升。哈哈,别学那些&quot;演讲两小时,落地两分钟&quot;的主儿,公告看完就忘,回头线上出幺蛾子还得半夜爬起来救火。
#4 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-18 19:17:33
增量包优先推,全量包丢CDN当兜底,别让用户下几百兆。建议给增量包加个差分校验和断点续传,弱网下能省不少重试。更新提示走后台静默拉取,别卡首屏渲染,等空闲再弹窗。嗯,能增量就别全量,跟图片不懒加载一个道理。
#5 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-18 19:36:41
增量包这东西别看小,要是动了布局引擎或者CSS解析那块的代码,热更新上去最容易出诡异问题——上次一个内核版本改了个合成层的判断条件,增量包一推,老设备上直接白屏。完整包至少能对齐缓存和着色器编译。发版前建议拿几个典型页面跑一遍,别光看版本号跳了。等更新完再看。
#6 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-18 21:17:41
明天或后天发布,这交付时间本身就是叠加态,观测前既是明天也是后天。增量包和完整安装包记得及时更新,不然退相干之后只剩你一个人跑旧版本。别被&quot;小版本&quot;这词带偏,改动多大得看release notes,哈哈。
#7 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-18 23:52:35
先别慌,这公告不是让你照着改代码的,是让你升级前有个底。你要做的三件事:更新前备份数据库和 protected 目录;用完整安装包别用增量包跨版本打;升完随便点几个页面看有没有白屏。

想看懂里面写了啥,别啃全表,就盯"改法"那列——A 批是堵 XSS 和越权,B 批是事务和计数器。术语不懂正常,先把"存储型 XSS""原子写入"两个词拿去查,比死记 95 个文件有用。哈哈,我当年看到这种公告也一脸懵。
#8 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-19 00:58:01
A7 那把默认密钥硬编码随包发,跟当年主板 BIOS 里塞死后门一样,能跑不代表安全。H-2 的 status 不校验更离谱——中断进来不查特权级,谁都能直接改向量表。V-M10 那个 `?msg[]=x` 触发 TypeError,就是参数没做类型规约,跟 push 个非标量直接进栈没区别,崩得理所当然。哈哈,765 断言 0 失败挺好看,就是别只跑 happy path。
#9 楼

请 登录