[审计]代码安全审查备忘录

👑Lv.11 元老 🌞 骨干会员
2026-10-25 00:02:12

本文由 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 文件可直接被执行

修复方案:

  1. 解压前遍历 ZIP 内文件,拒绝任何包含 ../ 或 .php 后缀的文件
  2. 解压后落地文件做白名单后缀校验(仅 .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 层的整体设计比预想中要好——该有的分层都有,职责清晰。问题集中在安全细节上:

  1. XSS 防线全靠自觉 — 当前是开发者手动 htmlspecialchars(),漏一处就是漏洞。好在 TemplateCompiler 的 {{var}} 标签编译为 echo htmlspecialchars(...),所以模板中写 {{变量}} 是自动转义的。但 Controller 传值时预转义和模板转义叠加可能导致双重转义。需要检查模板使用 {{var}} 还是直接 echo。
  2. sanitizeOrderBy 当前正则 /[^a-zA-Z0-9_.,\s\]/ 允许了反引号 `,意味着列名可被注为 id -- 1`` 这种形式。建议去掉反引号。
  3. Router 的遍历匹配 对当前项目规模完全不成问题。
  4. 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 实际业务逻辑最密集的层,问题分三类:

致命(需优先修):

  1. Upload.php 黑名单可被 .pht / .shtml / 大写后缀绕过 → 服务器沦陷
  2. Plugin.php include $hookFile 无路径白名单 → 任意文件包含 RCE
  3. Search.php 业务请求中执行 ALTER TABLE → 大表锁死全站

重要:

  1. Settings.php 写文件缺 LOCK_EX → 缓存损坏 500
  2. Content.php Base64 盲猜误判 → 正常文本变乱码
  3. Vote.php IN 子句拼装错误 → 批量查询只返回一条

优化建议:

  1. Tag.php getAll() 全表扫描性能问题(数据量大后出现)
  2. Theme.php 正则 \r 残留问题
  3. 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 层整体质量不错——延迟关联优化、级联清理设计都体现出经验。问题集中在三处:

致命(必须优先修):

  1. User.php 的 $this->hardDelete($t['id']) → 帖子 ID 传给用户删除方法 → 无限递归死循环,CPU 100% 挂起。这是这批审查中最危险的 Bug。
  2. Setting.php 的 $qc 未定义变量 → 后台设置保存直接崩溃

重要:

  1. Blog.php 无条件 Content::decode() → 正常文章变乱码
  2. 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、纯原生、按需加载、多模式降级,这些设计理念很现代。问题集中且容易修复:

必须修:

  1. vote.js 缺 CSRF Token → 点赞功能实际不可用(后端会拒绝)
  2. editor.js quote 功能缺转义 → DOM-XSS

建议修:

  1. execCommand 废弃问题(当前可用,长期需迁移)
  2. 异常处理流优化

对比后端的安全漏洞(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() 改为空方法解决


我的想法

这批联动问题与前几轮审查的问题有重叠:

  1. 10.2 二次验证超时 ↔ 2.1(之前已记录,修复方案相同)
  2. 10.6 验证码重放 ↔ 7.2(之前已记录)
  3. 10.8 退出登录 → 新发现,logout 流程确实有 Bug:销毁了 Session 但没清 Cookie
  4. 10.1 管理员降级保护 → 新发现,保护逻辑写了检查但没中断执行,是个典型的"写了但没生效" Bug
  5. 10.7 附件清理误伤 → 新发现,应该加时间缓冲

整体来看,新增的发现不多,大部分与前几轮重叠。最值得注意的新问题是 10.1(管理员自降级) 和 10.8(永远退不出登录),都是用户能直接感知的逻辑 Bug。

轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 浏览 2 次 | 回复 0 次

全部回复 (0)

暂无回复

请 登录