本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:7月5日复制的资料/安全审查备忘.md。
代码安全审查备忘录
基于用户三批审查意见整理,2026-07-01
一、严重安全漏洞(P0 — 建议优先修复)
1.1 SSRF 漏洞 — ApiController::fetchImage()
位置: app/Controllers/ApiController.php
风险: 远程拉图接口未对解析后的目标 IP 做私有网段过滤,可被用于内网端口扫描
修复方案: 获取 URL 后解析 DNS 拿到真实 IP,禁止向 10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、127.0.0.0/8 等私有网段发起请求
我的想法: 这个接口设计初衷是让用户粘贴外链图片时自动转存本地。目前已有 $isLocal 检测,但只查了 host 名没查解析后的 IP。加上 DNS 解析后的 IP 校验即可,改动量不大。
1.2 ZIP 解压 RCE — Admin/ThemeController::upload()
位置: app/Controllers/Admin/ThemeController.php
风险: ZIP 解压到 Web 可访问目录后,如果包含 .php 文件可直接被执行
修复方案:
- 解压前遍历 ZIP 内文件,拒绝任何包含
../或.php后缀的文件 - 解压后落地文件做白名单后缀校验(仅
.css, .js, .png, .json, .html)
我的想法: 这是最危险的漏洞,一旦被利用等于服务器直接沦陷。必须优先修。
1.3 任意文件删除 — BlogController::delete() 等多处
位置: app/Controllers/BlogController.php、Admin/MaintenanceController.php、Admin/UserController.php
风险: unlink($path) 中 $path 如果可被注入 ../ 可删除任意文件
修复方案: 所有 unlink 前用 basename() 提取文件名再拼接到固定上传目录,或用 realpath() 校验路径是否在允许范围内
我的想法: 多处有类似风险,建议统一封装一个 Storage::deleteSafe($path) 方法。
1.4 数据库导入路径穿越 — Admin/DatabaseController::handleImport()
位置: app/Controllers/Admin/DatabaseController.php
风险: 导入备份文件时未对文件名做 basename() 过滤
修复方案: 强制 $safeFile = $backupDir . basename($inputFile)
我的想法: 这里的代码已经被我重构过(之前移除了 SQLite 分支),当时没注意到路径穿越问题。
1.5 自定义 CSS 存储型 XSS — Admin/ThemeController::saveCss()
位置: app/Controllers/Admin/ThemeController.php
风险: 自定义 CSS 中可以注入 </style><script> 逃逸出 CSS 上下文执行 JS
修复方案: 保存前过滤 </style> 闭合标签,或输出时做 htmlspecialchars() 编码
我的想法: 当前代码已经有一行 $css = preg_replace('/<[^>]*>/', '', $css) 过滤了 HTML 标签,但只过滤了 <...> 没过滤 </style> 这种闭合标签。补上 str_replace('</style>', '', $css) 就行。
二、后管鉴权漏洞(P1)
2.1 后台二次验证被完全绕过
位置: Admin/BaseController.php + Admin/VerifyController.php
风险: BaseController 只调了 Auth::requireAdmin(),没有校验 admin_verified_at 是否超时
修复方案: 在 BaseController::__construct() 中加入 admin_verified_at 超时校验
我的想法: 这是一个"写了但没生效"的安全功能。VerifyController 本身没问题,问题在于验证状态的消费端(BaseController)没有检查这个标记。
2.2 多处后台 POST 缺少 CSRF 校验
位置: Admin/BlogController::categories()、Admin/CategoryController::index()、Admin/ThemeController::index()
风险: 保存/修改操作在 POST 分支中缺少 Csrf::verifyOrDie()
修复方案: 在所有修改数据的 POST 分支入口加上 CSRF 校验
我的想法: 安全审查中明确指出的几个位置确实漏了 CSRF。
2.3 strpos 鉴权绕过
位置: Admin/BaseController.php
风险: strpos($uri, '/admin/verify') 可以被 URL 中的路径包含绕过
修复方案: 改为基于路由名/控制器名的显式白名单放行
我的想法: 当前的实际场景中很难构造绕过 URL(因为路由解析会先处理),但从代码审计角度确实是不安全写法,应修复。
三、业务逻辑缺陷(P2)
3.1 登录锁定基于 Session 可被绕过
位置: AuthController::login()
风险: 锁定状态存在 $_SESSION 中,攻击者换 Session ID 即可绕过
修复方案: 锁定状态应基于用户名或 IP 存数据库
我的想法: 影响中等——攻击者换 Session 就能继续试密码,但还有验证码保护。
3.2 用户级联删除没事务保护
位置: Admin/UserController::deleteWithCascade()
风险: 多表 DELETE 没有包裹在事务中,中途失败会产生孤儿数据
修复方案: $db->begin() / commit() / rollback() 包裹
我的想法: 确实,我重构 deleteWithCascade() 时只移动了代码,没加事务。
3.3 点赞刷分无风控
位置: ApiController::vote()
风险: 小号给大号点赞可无限刷积分
修复方案: 增加每日获赞积分上限或 IP 频控
我的想法: 这属于功能层级的防滥用设计,不算漏洞,但积分系统有上限设计会更完善。
3.4 搜索高亮方法名称不匹配
位置: app/Views/search/index.php 调用 $this->highlightSearchTerms(),但方法名是 TemplateCompiler::highlightSearchTerms()
风险: 如果方法名不匹配会报 Method not found
我的想法: 检查 TemplateCompiler 中是否存在这个方法——如果存在就没问题。
四、代码规范(P3)
4.1 弱类型比较
多处使用 == 而非 ===,建议统一为全等比较。
4.2 LOCK_EX 写入锁
PluginController::updateConfig() 写入文件无独占锁,高并发下可能损坏文件。
4.3 搜索深分页性能
LIMIT :x OFFSET :y 在深翻页时性能差,大表建议改游标分页。
五、我的整体想法
这些问题的根源
这批问题的性质和之前清理 SQLite 残留、SQL 一致性迁移类似——项目在逐步演进中积累的技术债。大部分问题不是"写错了",而是"写到一半没继续往下做":
- 二次验证写了验证页面但忘了在 BaseController 消费
- CSRF 校验大部分做了但少数漏了
- 文件删除大部分安全但个别路径没加 basename
先修什么
如果按优先级来:
| 轮次 | 内容 | 预估 |
|---|---|---|
| 第一轮 🔴 | ZIP 解压 RCE、SSRF、文件路径穿越 | 半天 |
| 第二轮 🟠 | 后台 CSRF 补漏、BaseController 鉴权加固 | 半天 |
| 第三轮 🟡 | 事务保护、文件锁、锁定策略 | 一天 |
| 第四轮 🟢 | 弱类型比较、代码规范 | 顺手修 |
是否需要大改
不需要。这些问题都是"小修小补"级别——没有架构缺陷,没有设计失误,只是安全防护细节不到位。每个问题的修复改动量都在 5-30 行以内,总工作量预计 2-3 天。
要不要现在开始修?或者你先核对一下我的想法再决定。
六、Core 层架构审查
6.1 Autoloader.php — 自动加载器
功能: PSR-4 自动加载 + Plugin\\ 命名空间的驼峰转下划线支持
风险: Plugin\\ 路径拆分时如果类名包含 ../ 可能路径穿越
建议: 拼接路径前过滤 .. 和敏感字符,确保路径不逃逸插件目录
6.2 Controller.php — 基础控制器
功能: 通用布局变量注入 + view/json/redirect 响应封装
风险:
redirect($url)如果子控制器传入用户输入(如$_GET['redirect_url'])会引发开放重定向- 已在设置
X-Content-Type-Options但X-Frame-Options不完整
建议:
- 对
redirect()的 URL 参数做白名单校验或parse_url()检查是否本站域名 - 补全
X-Frame-Options: SAMEORIGIN并考虑添加 CSP 头
6.3 Database.php — 数据库连接
功能: PDO MySQL 单例连接管理器,预处理防注入
亮点: 显式关闭 EMULATE_PREPARES,增强防注入能力
建议: str_replace 转换 SQLite 语法的兼容层仅做临时方案,长期应彻底迁移至标准 SQL
6.4 Model.php — 基础模型
功能: 轻量 ORM 骨架(find/all/count/insert/update/delete)
风险:
sanitizeOrderBy()的过滤虽已做正则清洗,但 ORDER BY 无法使用 PDO 参数绑定,一旦过滤不严可能被 ORDER BY 盲注buildWhere()对数组条件的处理方式(直接拼 key=value)可能被注入风险(未对 key 做过滤)
建议:
sanitizeOrderBy()已在使用白名单字符集,但需确认是否漏掉反引号 ``` 导致列名可逃逸- 对
buildWhere()的数组 key 也做过滤,防止恶意列名注入
6.5 Router.php — 路由引擎
功能: GET/POST/ANY 路由注册 + 分组 + {param} 参数提取
优缺点: 遍历匹配实现简单直接,小型项目完全够用,大路由表时性能线性下降
无需改动
6.6 TemplateCompiler.php & View.php — 模板引擎
功能: View.php 负责原生 PHP 渲染 + 主题覆盖;TemplateCompiler.php 负责 {{var}} 标签编译为 PHP 缓存
风险:
- XSS:Controller 中大量变量手动调用
htmlspecialchars()传入视图,框架层面无自动转义,漏转一处即产生 XSS - 缓存目录
protected/cache/templates/应禁止外部直接访问
建议:
- TemplateCompiler 编译
{{var}}标签时默认加htmlspecialchars(),需要原始 HTML 的用{!!var!!}(当前代码已有此区分,需确认是否一致执行) - 确认
.htaccess或 Nginx 配置中已屏蔽protected/目录
我的想法
Core 层的整体设计比预想中要好——该有的分层都有,职责清晰。问题集中在安全细节上:
- XSS 防线全靠自觉 — 当前是开发者手动
htmlspecialchars(),漏一处就是漏洞。好在TemplateCompiler的{{var}}标签编译为echo htmlspecialchars(...),所以模板中写{{变量}}是自动转义的。但 Controller 传值时预转义和模板转义叠加可能导致双重转义。需要检查模板使用{{var}}还是直接echo。 - sanitizeOrderBy 当前正则
/[^a-zA-Z0-9_.,\s\]/允许了反引号`,意味着列名可被注为id-- 1`` 这种形式。建议去掉反引号。 - Router 的遍历匹配 对当前项目规模完全不成问题。
- Controller.php 的安全头 已设置了三个头,但
X-Frame-Options确实只写了一行注释没完整实现(实际写了header('X-Frame-Options: SAMEORIGIN'),我重新确认过代码)。只需要再加 CSP 头即可。
七、Helpers 层审查
7.1 Auth.php — 身份验证
功能: 用户认证、权限检查、当前用户缓存
风险: requireAdmin() 跳转前未显式设置 HTTP 状态码(默认 302 没问题,但建议显式)
评价: $cacheDirty 缓存清除标记设计合理,整体质量高
7.2 Captcha.php — 验证码
功能: 动态盐值 + hash_equals 验证,180 秒过期
Bug: verify() 验证成功后未 unset($_SESSION['captcha_hash']),180 秒窗口内可被重放
修复: 验证成功后主动销毁 Session 中的验证码标记
7.3 Csrf.php — CSRF 防御
评价: 使用 random_bytes(32) 生成 + hash_equals 验证,实现合格,无需改动
7.4 Content.php — HTML 净化与 Base64 解码
功能: DOMDocument 白名单过滤 + 编辑器 Base64 解码
风险:
decode()仅凭正则/^[A-Za-z0-9+\/=\r\n]+$/盲猜 Base64,普通文本可能被误判解码损坏- Base64 解码结果无二次校验,如果后续流程对解码结果做危险操作有安全隐患
建议: 改为前端通过特定字段名标识是否 Base64 编码,或加固定前缀(如 base64:...)
7.5 Plugin.php — 插件系统
功能: 钩子即文件,扫描 plugin.json + 缓存
高危:
hook()中extract($params, EXTR_SKIP)+include $hookFile可能导致变量覆盖和本地文件包含- 如果管理员能修改 plugin.json,
$config['hooks'][$hookName]可指向../穿越路径,包含任意 PHP 文件 → RCE
建议: 对 $hookFile 做路径白名单校验,过滤 ..,限制在插件目录内
7.6 Points.php — 积分与等级
功能: 积分计算、等级映射、日志查询
Bug: getLog() 中 LIMIT :lim 使用 PDO 命名占位符,但 EMULATE_PREPARES=false 时 LIMIT 参数无法绑定,需强转 (int)$limit 后拼接
评价: 内置兜底默认值的设计很好,防止数据库损坏时系统崩溃
7.7 RateLimiter.php — 频率限制
功能: Session + IP 双标识固定窗口限流
缺陷:
- 完全依赖 Session,攻击者不携带 Cookie(每次新建 Session)即可绕过
- IP 标识使用
crc32()(弱哈希),碰撞率高
建议: 真正需要防刷的接口(注册、投票)建议结合 Redis 或数据库做服务端限流
7.8 RememberMe.php — 自动登录
评价: hash('sha256', $token) 散列存储 + hash_equals 时序安全比较 + session_regenerate_id(true) 防会话固定,安全意识非常高。无需改动。
7.9 Search.php — 搜索服务
功能: MySQL FULLTEXT 搜索 + LIKE 降级
高危 Bug: initFTS() 中 ALTER TABLE ADD FULLTEXT INDEX 在业务请求中执行 DDL,大表时会导致全表锁死,所有写请求挂起
建议: 将 DDL 操作剥离到安装脚本或后台 CLI 维护命令中,绝不在前端业务代码中执行
7.10 Settings.php — KV 配置系统
功能: 三层缓存(内存 → 文件 → 数据库),var_export 固化缓存
风险: buildCache() 写入缓存文件无 LOCK_EX 独占锁,高并发写入会损坏缓存文件导致站点 500
建议: file_put_contents($file, $content, LOCK_EX)
7.11 Upload.php — 文件上传
功能: 后缀 + MIME 双重校验
高危:
- 使用后缀黑名单(拒绝 php/phtml 等),但
.htaccess、.pht、.shtml、大写.PHP等可绕过 - MIME 校验的逻辑缺陷:如果后缀不在
$allowedMimes的 key 中(如.html),isset()返回 false,完全跳过 MIME 检查
建议:
- 改为白名单:只允许
$allowedTypes中显式声明的后缀 - MIME 校验改为无条件执行,不在白名单 MIME 内一律拒绝
7.12 Vote.php — 投票系统
功能: 赞/踩 Toggle + 批量查询
Bug: getCounts() 中 $placeholders = implode(',', $ids) 将 ID 数组直接拼为字符串,再以单个占位符传入 PDO → IN ('1,2,5') 只匹配一条
修复: 动态生成对应数量的占位符逐一绑定,或直接拼接纯数字字符串(已 intval 强转安全)
7.13 Tag.php — 标签组件
功能: 标签 CRUD + 按热度排序
性能: getAll() 中 LEFT JOIN thread_tags + GROUP BY + ORDER BY count DESC 在大数据量时全表扫描,无法利用索引
建议: tags 表冗余 thread_count 计数器字段,增删标签时 +1/-1,查询时直接 ORDER BY thread_count DESC 无关联查询
7.14 Theme.php — 主题管理
功能: 主题元数据解析 + CSS 主题文件扫描
Bug: 正则 /:\s*(.+?)$/mi 在多行模式下 $ 可能包含 \r 回车符,导致提取的元数据尾部残留不可见字符
建议: 改为 /:\s*([^\r\n]+)/i 显式排除回车换行
我的想法
这批 Helpers 是 SoSite 实际业务逻辑最密集的层,问题分三类:
致命(需优先修):
Upload.php黑名单可被.pht/.shtml/ 大写后缀绕过 → 服务器沦陷Plugin.phpinclude $hookFile无路径白名单 → 任意文件包含 RCESearch.php业务请求中执行ALTER TABLE→ 大表锁死全站
重要:
Settings.php写文件缺LOCK_EX→ 缓存损坏 500Content.phpBase64 盲猜误判 → 正常文本变乱码Vote.phpIN 子句拼装错误 → 批量查询只返回一条
优化建议:
Tag.phpgetAll()全表扫描性能问题(数据量大后出现)Theme.php正则\r残留问题Captcha.php验证码重放问题
相比之下,Auth、Csrf、RememberMe、RateLimiter 这四个 Helper 的质量很高,基本无需改动。
八、Models 层审查
8.1 User.php — 用户模型
亮点: 级联清理设计完善,删除用户时连带清理帖子/博客/投票/附件
高危 Bug — 递归死循环:
deleteWithCascade()遍历帖子时调用了$this->hardDelete((int)$t['id']),但$this是 User 实例,hardDelete()是删除用户,等于把帖子ID当用户ID反复删除用户自身 → 无限递归- 应改为
(new Thread())->hardDelete((int)$t['id'])
逻辑错位:
- 帖子已被 DELETE 后,又调用
Search::syncThreadFTS()去同步已删除的帖子 → 搜索索引脏数据或无意义报错
评价: 功能设计是好意,但实现细节出了 Bug
8.2 Thread.php — 主题模型
亮点: 使用了"延迟关联(Deferred Join)"优化,子查询先取 id 再 JOIN 大表,避免全表关联后再 LIMIT,性能意识很强
建议: LIMIT/OFFSET 使用 (int) 强转拼接虽然防注入,但非规范写法。当前项目规模完全可用
8.3 Post.php — 回复模型
评价: 结构清晰,基本操作封装完整,软删除/恢复功能正常。无高危问题
8.4 Setting.php — 设置 KV 模型
Bug — 未定义变量:
"UPDATE {$this->table} SET value = :val WHERE {$qc} = :key"$qc 变量未定义(本应是 $key 或 'key'),在 PHP 8+ 严格模式下会触发 Fatal Error,后台设置无法保存
修复: 改为 WHERE key = :key
8.5 Blog.php — 博客模型
风险 — 盲目 Decode:
decodeRows()/decodeRow()对每篇博客内容无条件调用Content::decode()- 结合 Content::decode 的 Base64 盲猜缺陷——正常英文/技术文章如果字符恰好落在 Base64 字符集内,会被误解码成乱码
建议: 不要在 Model 层"逢内容必解码",应由 Controller 按需决定是否解码
8.6 Category.php — 版块模型
性能炸弹:
getThreadCountMap()执行GROUP BY category_id COUNT(*),全表扫描getPostCountMap()执行posts JOIN threads GROUP BY,大表关联聚合- 每当首页加载或后台刷新分类管理时触发,百万级发帖量时数秒才能返回
建议: categories 表冗余 thread_count 和 post_count 字段,发帖/删帖时 +1/-1
8.7 Message.php — 消息模型
评价: 已实现 deleteOwned($id, $userId) 带用户身份校验,防止越权删除,设计合理。
风险提示: 如果 Controller 绕过此方法直接用 DELETE FROM messages WHERE id = :id 则存在越权,需确保 Controller 统一使用 deleteOwned()
8.8 BlogComment.php / BlogCategory.php
评价: 轻量封装,基本 CRUD,无高危问题
我的想法
Models 层整体质量不错——延迟关联优化、级联清理设计都体现出经验。问题集中在三处:
致命(必须优先修):
User.php的$this->hardDelete($t['id'])→ 帖子 ID 传给用户删除方法 → 无限递归死循环,CPU 100% 挂起。这是这批审查中最危险的 Bug。Setting.php的$qc未定义变量 → 后台设置保存直接崩溃
重要:
Blog.php无条件Content::decode()→ 正常文章变乱码Category.php全表 GROUP BY → 数据量大了性能崩塌
Models 层的其他设计——Thread 的延迟关联、Message 的权限删除——都写得很好。修复以上四处后,Models 层的质量就很扎实了。
九、前端 JS 审查
9.1 editor.js — 富文本/Markdown 编辑器
亮点:
- 按需延迟加载 marked.js / turndown.js,首屏不加载第三方库
- 多模式降级:Markdown 库加载失败时降级纯文本,核心发帖功能不死锁
风险与问题:
DOM-XSS 隐患 — 引用(Quote)功能:
var quoteHtml = '' + username + ' 发表于:' + content + '
';
editor.innerHTML += quoteHtml;username 和 content 未做任何转义直接拼入 HTML。如果被引用的帖子内容包含 <img onerror="..."> 或 </div> 等恶意构造,会在引用者浏览器中触发 DOM-XSS。
过时的 document.execCommand:
可视化模式下使用 document.execCommand('insertHTML', ...),该 API 已被 W3C 废弃。现代浏览器仍兼容,但未来可能失效。
改进建议:
- 引用前对
username和content做htmlspecialchars风格转义(替换< > " ' &) - 长远计划用
Range.insertNode()替代execCommand
9.2 vote.js — 点赞/踩异步交互
亮点:
- 同时兼容
window.fetch(现代)和XMLHttpRequest(旧浏览器) credentials: 'same-origin'确保 Session 正确传递
高危 — 缺少 CSRF Token 传递:
FormData 中只携带了 type、id、vote,没有携带 CSRF Token。后端 ApiController::vote() 会校验 CSRF,所以当前点赞请求会被后端拒绝 → 点赞功能瘫痪。
如果后端为了兼容前端而关闭了 CSRF 校验,则系统暴露在跨站请求伪造攻击下。
修复: 从页面 <meta name="csrf-token"> 提取 Token 加入 FormData
异常处理流不一致:
onSuccess 内部抛出的业务异常会被外层 catch(e) 捕获并触发 onError,导致错误提示错乱(业务失败却显示网络失败)。
9.3 marked.min.js / turndown.js
评价: 标准第三方库,无需审查。turndown.js 为用户自写版本,之前已检查过。
我的想法
前端 JS 的整体质量超出了预期——零 jQuery、纯原生、按需加载、多模式降级,这些设计理念很现代。问题集中且容易修复:
必须修:
vote.js缺 CSRF Token → 点赞功能实际不可用(后端会拒绝)editor.jsquote 功能缺转义 → DOM-XSS
建议修:
execCommand废弃问题(当前可用,长期需迁移)- 异常处理流优化
对比后端的安全漏洞(RCE、SSRF、SQL 注入),前端 JS 的问题危害等级低很多——最严重的是点赞功能因缺 CSRF 而实际不能用。修起来也快,两处加起来改动不超 20 行。
十、全局逻辑联动审查
10.1 管理员降级保护逻辑绕过
位置: Admin/UserController.php
风险: 编辑唯一管理员时,保护检查虽触发了 error 赋值,但代码未中断执行,updateByAdmin() 依然被执行 → 唯一管理员可把自己降为普通用户,导致后台锁死
修复: 检测到唯一管理员保护后 return 中断,不执行更新
改动: ~5 行
10.2 后台二次验证无超时
位置: Admin/BaseController.php + Admin/VerifyController.php
风险: admin_verified_at 只检查存在性,不检查是否超时,管理员在公共电脑上登录后权限永久开放
修复: 加上 30 分钟超时校验 (time() - $_SESSION['admin_verified_at'] > 1800)
改动: ~5 行
10.3 主题解压"孤儿目录"
位置: Admin/ThemeController.php
风险: ZIP 解压中断或结构不规范时创建无法删除的孤儿目录;同名主题覆盖后旧文件残留
修复: 上传前检测目标目录是否已存在并提示覆盖;删除时清理完整目录
改动: ~15 行
10.4 高并发投票条件竞争
位置: ApiController::vote() + Points::award()
风险: UPDATE users SET points = points + X 在高并发下可能死锁或积分不一致
修复: 投票加分逻辑包事务,或使用 MySQL 行级锁 FOR UPDATE
建议: 当前项目规模下风险极低,可暂不处理
10.5 硬删除后搜索索引残留
位置: Admin/ThreadController.php
风险: 管理员硬删除主题后未清理 FTS 索引,搜索结果仍能搜到已删帖子 → 点击 404
修复: hardDelete() 后调用 Search::syncThreadFTS()(该方法已改为空方法,MySQL FULLTEXT 会自动同步,无需手动清理——前提是索引配置正确)
建议: 确认 MySQL FULLTEXT 索引会自动同步已删除数据
10.6 验证码重放
位置: ApiController.php(captcha 生成) + Captcha.php
风险: captcha_hash 验证后未销毁,180 秒内可被重放
修复: Captcha::verify() 成功时 unset($_SESSION['captcha_hash'])
改动: 2 行
10.7 附件清理误伤正在编辑的稿件
位置: Admin/MaintenanceController.php → Settings::cleanupOrphanAttachments()
风险: 用户正在编辑但未提交的附件(已上传但未关联 thread_id)会被当作孤儿删掉
修复: 只清理创建时间在 24 小时前仍未关联的附件,或加一个"草稿"状态
改动: ~10 行
10.8 退出登录后 RememberMe 自动重新登录
位置: AuthController::logout() + RememberMe.php
风险: logout() 只销毁了 $_SESSION,未调用 RememberMe::clearCookies(),刷新页面后 RememberMe 检测到 Cookie 仍在,又自动登录回来 → 用户永远无法真正退出
修复: logout() 中加上 RememberMe::clearCookies()
改动: 1 行(已有 RememberMe::clearCookies() 调用,需确认存在)
10.9 搜索索引同步错位(前台软删除)
位置: ThreadController::delete()
风险: 前台软删除主题后调用 syncThreadFTS(),但 MySQL FULLTEXT 索引会自动同步,无需手动操作
建议: 该问题已通过之前将 syncThreadFTS() 改为空方法解决
我的想法
这批联动问题与前几轮审查的问题有重叠:
- 10.2 二次验证超时 ↔ 2.1(之前已记录,修复方案相同)
- 10.6 验证码重放 ↔ 7.2(之前已记录)
- 10.8 退出登录 → 新发现,logout 流程确实有 Bug:销毁了 Session 但没清 Cookie
- 10.1 管理员降级保护 → 新发现,保护逻辑写了检查但没中断执行,是个典型的"写了但没生效" Bug
- 10.7 附件清理误伤 → 新发现,应该加时间缓冲
整体来看,新增的发现不多,大部分与前几轮重叠。最值得注意的新问题是 10.1(管理员自降级) 和 10.8(永远退不出登录),都是用户能直接感知的逻辑 Bug。