本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:备忘md/code_review_report_20260621.md。
SoSite V3.0.0 代码审查报告(更新版)
审查日期:2026-06-21(更新)
审查范围:95+ PHP 文件(50 个控制器/模型 + 13 个 Helpers + 6 个 Core + 视图模板 + 入口文件)
审查方法:逐文件阅读,追踪数据流(Source → Sink),检查安全边界和逻辑正确性
总体评分:良好 ✅
核心安全机制健全:
- 全部 SQL 使用参数化绑定,无字符串拼接(
:param风格) - 模板引擎
{$var}自动htmlspecialchars()转义 - CSRF Token 在关键写操作均有校验
- 密码使用
password_hash(PASSWORD_DEFAULT)+ 恒定时间password_verify() - 文件上传有 MIME 校验 + 扩展名白名单 + 危险扩展拦截
- 会话 Cookie 已设
httponly、SameSite=Lax、secure
发现的问题
① FTS 同步使用字符串内联 SQL(低风险)
| 项目 | 内容 |
|---|---|
| 文件 | app/Helpers/Search.php — syncThreadFTS() / syncBlogFTS() (L182-198, L203-219) |
| 代码 | $conn->exec("DELETE FROM threads_fts WHERE rowid = {$threadId}") 使用了字符串内插而非参数绑定 |
| 风险 | 虽然 $threadId 已被 (int) 强制类型转换,SQL 注入不可行,但这是不安全的编码模式,未来维护者可能复制此模式到其他场景 |
| 建议 | 改为参数化绑定 |
② highlightTerms() 双编码 + @ 抑制错误(低风险)
| 项目 | 内容 |
|---|---|
| 文件 | app/Helpers/Search.php — highlightTerms() (L144-162) |
| 描述 | @preg_replace 抑制错误,函数内部对已转义文本再次调用 htmlspecialchars |
| 建议 | 移除 @ 错误抑制符,确认双编码场景 |
③ 后台删除操作通过 GET 传递 CSRF Token(中风险)⚠️
| 项目 | 内容 |
|---|---|
| 涉及文件 | 5 个文件 |
| 代码模式 | 使用 GET 请求 + URL 中的 csrf token 执行删除。CSRF Token 在 URL 中可通过 Referer 头泄露 |
| 风险等级 | 中等 - CSRF Token 在 URL 中容易被日志记录、浏览器历史、第三方服务引用等场景泄露 |
涉及文件列表:
app/Controllers/Admin/ThreadController.php— 主题删除 (L10-L13)app/Controllers/Admin/UserController.php— 用户删除 (L44-L51)app/Controllers/Admin/CategoryController.php— 分类删除 (L37-L41)app/Controllers/Admin/TagController.php— 标签删除 (L10-L16)app/Controllers/Admin/DatabaseController.php— 备份/优化操作 (L52-L55, L78-L81, L95-L98)
建议:改为 POST 请求执行删除操作
④ SettingsController 无输入验证(低风险)
| 项目 | 内容 |
|---|---|
| 文件 | app/Controllers/Admin/SettingsController.php (L12-L17) |
| 代码 | $_POST[$key] 直接存储,无长度/内容校验 |
| 建议 | 增加长度限制和 HTML 标签剥离(参考 ThemeController::saveCss() 的做法)
⑤ ThemeController::delete() 使用 ReflectionClass 清空私有静态属性(设计问题)
| 项目 | 内容 |
|---|---|
| 文件 | app/Controllers/Admin/ThemeController.php |
| 描述 | 通过反射访问 Theme::$cachedThemes 私有属性 |
| 建议 | Theme 类增加 public static function clearCache() 公开方法 |
⑥ TemplateCompiler 缓存写入非原子操作(低风险)
| 项目 | 内容 |
|---|---|
| 文件 | app/Core/TemplateCompiler.php — renderOne() (L203-L207) |
| 描述 | file_put_contents($cacheFile, $php) — 非原子写入,并发时可能产生不完整缓存文件 |
| 建议 | 采用 .tmp + rename 模式(如 Settings::buildCache() 的做法)
⑦ 后台 VerifyController 未限制尝试次数(低风险)
| 项目 | 内容 |
|---|---|
| 文件 | app/Controllers/Admin/VerifyController.php (L8-L17) |
| 描述 | 管理员二次密码验证无速率限制 |
| 建议 | 增加 Session 或 IP 级别的尝试次数限制
逻辑问题
⑧ 用户删除不检查唯一管理员(逻辑问题)⚠️
| 项目 | 内容 |
|---|---|
| 文件 | app/Controllers/Admin/UserController.php (L44-L51) |
| 描述 | 仅检查 $uid > 1 保护 ID 为 1 的用户。如果后来 admin 角色给了其他用户,ID 为 1 的用户被删除后系统将无管理员 |
| 建议 | 删除前检查被删用户是否为管理员且是否为最后一个管理员
⑨ Vote.php 中 SQL 风格不一致(低风险)
| 项目 | 内容 |
|---|---|
| 文件 | app/Helpers/Vote.php (L45-L47) |
| 描述 | LIMIT 子句使用字符串内联(已 (int) 强转安全),与代码库其他处的参数化风格不一致 |
| 建议 | 改为参数化绑定
⑩ 登录锁定仅基于 Session(低风险)
| 项目 | 内容 |
|---|---|
| 文件 | app/Controllers/AuthController.php |
| 风险 | 攻击者可清 Cookie 绕过锁定 |
| 建议 | 结合 IP 进行锁定
⑪ 登录恒定时间比较中的 dummyHash 使用 static 变量(低风险)
| 项目 | 内容 |
|---|---|
| 文件 | app/Controllers/AuthController.php |
| 描述 | static $dummyHash = null 使用 static 缓存 dummyHash,无实际安全影响 |
| 建议 | 可移除,不影响安全性
修复优先级建议
🔴 高优先级(建议立即修复)
- 问题③:5 个后台删除操作改用 POST 请求(涉及面最广,风险最高)
🟡 中优先级(建议近期修复)
- 问题⑧:用户删除检查唯一管理员
🟢 低优先级(可逐步优化)
- 问题①、②、④、⑤、⑥、⑦、⑨、⑩、⑪
汇总
| 项目 | 值 |
|---|---|
| 审查文件数 | 95+ PHP 文件 |
| 安全评分 | 85 / 100 |
| 直接可利用漏洞 | 0(HIGH / MEDIUM / LOW 均无) |
| 防御层面建议 | 11 项 |
重点关注:问题③ 后台多个删除操作用 GET 传 CSRF Token,涉及面最广(5 个文件),建议优先处理。