[修复]SoSite 安全修复计划

👑Lv.11 元老 🌞 骨干会员
2026-10-24 23:58:56

本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:7月5日复制的资料/安全修复计划.md。

SoSite 安全修复计划

基于全部代码审查整理,2026-07-01

覆盖范围:Controllers / Core / Helpers / Models / JS / 全局联动逻辑


总览

审查覆盖了除视图模板外的几乎所有 PHP 代码及前端 JS,共发现 约 35 个问题,按风险等级分为四档。

总工作量估计:4-6 天(每天 3-4 小时),逐项修复,每项改动量 1-30 行。


第一轮 — 🔴 致命漏洞(服务器沦陷风险)

这些漏洞一旦被利用,攻击者可以直接控制服务器或删除关键文件,建议最先修复。

1.1 ZIP 解压 RCE — Admin/ThemeController::upload()

项内容
风险ZIP 解压到 Web 可访问目录后,内含的 .php 后门可直接被执行
修复解压前遍历 ZIP 内文件名,拒绝包含 ../ 或 .php/.phtml/.php5 等后缀的文件;落地后仅保留白名单后缀
文件app/Controllers/Admin/ThemeController.php
改动~20 行

1.2 ZIP 主题孤儿目录与覆盖残留

项内容
风险同 1.1,解压中断产生无法删除的孤儿目录;同名主题覆盖后旧文件残留
修复上传前检测目标目录是否存在并提示;删除时完整清理目录结构
文件app/Controllers/Admin/ThemeController.php
改动合并到 1.1 一起修

1.3 Upload.php 黑名单绕过可上传脚本

项内容
风险使用后缀黑名单(禁 php/phtml),但 .htaccess/.pht/.shtml/大写 .PHP 可绕过,直接上传 Webshell
修复改为白名单:仅允许 $allowedTypes 中声明后缀;MIME 校验改为无条件执行
文件app/Helpers/Upload.php
改动~15 行

1.4 Plugin.php 任意文件包含 RCE

项内容
风险hook() 中 include $hookFile 无路径白名单,如果 plugin.json 被篡改可指向任意 PHP 文件
修复对 $hookFile 做 basename() + 限制在插件目录内
文件app/Helpers/Plugin.php
改动~10 行

1.5 SSRF — ApiController::fetchImage()

项内容
风险远程拉图接口未对解析后的 IP 做私有网段过滤,可被用于内网扫描
修复获取 URL 后解析 DNS 拿到真实 IP,禁止向 10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、127.0.0.0/8 等私有网段发请求
文件app/Controllers/ApiController.php
改动~15 行

1.6 User.php 递归死循环(系统崩溃)

项内容
风险deleteWithCascade() 中 $this->hardDelete($t['id']) 把帖子 ID 当用户 ID 反复删除用户自身 → 无限递归,CPU 100% 挂起
修复改为 (new Thread())->hardDelete((int)$t['id']);用户级联删除包事务
文件app/Models/User.php
改动~10 行(含事务保护)

1.7 Search.php 业务请求中执行 DDL

项内容
风险initFTS() 中 ALTER TABLE ADD FULLTEXT INDEX 在前端请求中执行,大表时会导致全表锁死
修复将 DDL 操作剥离到安装脚本或后台 CLI 维护命令中,前端只检测索引是否存在
文件app/Helpers/Search.php
改动~10 行


第二轮 — 🟠 高危漏洞(数据安全 / 越权 / 功能瘫痪)

这些漏洞不会直接导致服务器沦陷,但可能导致数据泄露、越权操作、功能瘫痪或关键逻辑绕过。

2.1 后台二次验证被完全绕过(无超时)

项内容
风险BaseController 只检查 admin_verified_at 存在性,不检查超时。管理员在公共电脑上权限永久开放;且二次验证状态未与 user_id 绑定
修复BaseController::__construct() 中加超时校验(30 分钟过期),验证状态绑定当前 user_id
文件app/Controllers/Admin/BaseController.php
改动~15 行

2.2 管理员自降级保护逻辑绕过

项内容
风险Admin/UserController 编辑唯一管理员时虽然触发了错误检查,但没有中断执行,updateByAdmin() 依然被执行 → 唯一管理员可把自己变为普通用户,后台锁死
修复检测到唯一管理员保护时 return 中断,不执行更新
文件app/Controllers/Admin/UserController.php
改动~5 行

2.3 vote.js 缺 CSRF Token → 点赞不可用

项内容
风险FormData 未携带 CSRF Token。后端需要 CSRF 校验,所有点赞请求被拒绝 → 点赞功能全站瘫痪。如果后端为迁就前端关闭校验,则暴露在跨站请求伪造下
修复从页面 <meta name="csrf-token"> 提取 Token 加入 FormData
文件assets/js/vote.js
改动~5 行

2.4 退出登录后 RememberMe 自动重新登录

项内容
风险logout() 只销毁了 $_SESSION,未调用 RememberMe::clearCookies()。刷新后 RememberMe 检测到 Cookie 仍在,自动登录回来 → 用户永远无法真正退出
修复logout() 中加上 RememberMe::clearCookies()
文件app/Controllers/AuthController.php
改动1 行(已有 clearCookies 方法,检查是否被调用)

2.5 多处后台 POST 缺 CSRF 校验

项内容
风险Admin/BlogController、Admin/CategoryController、Admin/ThemeController 的保存/修改 POST 分支缺少 CSRF 校验
修复在各 POST 分支入口加 Csrf::verifyOrDie()
文件3 个 Controller
改动每处 ~3 行

2.6 strpos 鉴权绕过

项内容
风险strpos($uri, '/admin/verify') 可通过 URL 构造绕过
修复改为基于当前路由/控制器名的白名单放行
文件Admin/BaseController.php
改动~10 行

2.7 任意文件删除(多处 unlink 无 basename)

项内容
风险BlogController::delete()、Admin/MaintenanceController、Admin/UserController 中的 unlink() 未对路径做白名单校验
修复所有 unlink 前用 basename() 提取文件名,或封装统一的安全删除方法
文件3 个文件
改动每处 ~5 行

2.8 数据库导入路径穿越

项内容
风险handleImport() 未对导入文件名做 basename() 过滤
修复$safeFile = $backupDir . basename($inputFile)
文件Admin/DatabaseController.php
改动1 行

2.9 editor.js quote DOM-XSS

项内容
风险引用功能中 username 和 content 未转义直接拼入 HTML。恶意用户可将用户名改成 <img onerror=alert(1)>,任何引用此人的用户都会触发 XSS
修复拼接前对 username 和 content 做 HTML 转义(替换 < > " ' &)
文件assets/js/editor.js
改动~10 行


第三轮 — 🟡 重要缺陷(功能异常 / 数据一致性问题)

这些问题不会直接产生安全漏洞,但会导致功能异常、数据损坏或体验严重下降。

3.1 Setting.php(Model)未定义变量 $qc

项内容
风险$qc 未定义,在 PHP 8+ 严格模式下触发 Fatal Error,后台设置页面无法保存
修复改为 WHERE \key\ = :key
文件app/Models/Setting.php
改动1 行

3.2 Content.php Base64 盲猜误判

项内容
风险仅凭正则盲猜 Base64,正常英文/技术文章可能被误判解码成乱码
修复改为前端通过特定字段标识是否 Base64,或增加固定前缀
文件app/Helpers/Content.php
改动~15 行

3.3 Blog.php 无条件 Content::decode()

项内容
风险Model 层对每篇博客无条件调用 Content::decode(),配合上述缺陷,正常文章变乱码
修复不在 Model 层解码,由 Controller 按需决定
文件app/Models/Blog.php
改动删掉 decodeRows/decodeRow 调用

3.4 Vote.php IN 子句拼装错误

项内容
风险$placeholders = implode(',', $ids) 再以单占位符传入 PDO,结果是 IN ('1,2,5') 只匹配一条
修复动态生成对应数量的占位符逐一绑定,或直接拼接已 intval 强转的数字
文件app/Helpers/Vote.php
改动~10 行

3.5 Settings.php 写缓存文件缺 LOCK_EX

项内容
风险buildCache() 写入无独占锁,高并发写入损坏缓存文件 → 站点 500
修复file_put_contents($file, $content, LOCK_EX)
文件app/Helpers/Settings.php
改动1 行

3.6 Points.php LIMIT 参数绑定问题

项内容
风险LIMIT :lim 使用 PDO 命名占位符,EMULATE_PREPARES=false 时无法绑定
修复改为 (int)$limit 直接拼接
文件app/Helpers/Points.php
改动1 行

3.7 附件清理误伤正在编辑的稿件

项内容
风险cleanupOrphanAttachments() 会把已上传但未关联 thread_id 的附件(用户正在编辑中)当作孤儿删掉,导致发帖后图片全裂
修复只清理创建时间在 24 小时前仍未关联的附件
文件app/Helpers/Settings.php
改动~10 行

3.8 高并发积分条件竞争

项内容
风险Points::award() 无事务保护,高并发投票时积分可能不一致
修复加分逻辑包事务或使用行级锁
文件app/Helpers/Points.php
改动~5 行
建议当前项目规模下风险极低,可暂缓处理


第四轮 — 🟢 建议优化(性能 / 规范 / 体验)

4.1 sanitizeOrderBy 允许反引号

项内容
风险当前正则 /([^a-zA-Z0-9_.,\s\])/` 允许了反引号,列名可被注入
修复去掉反引号,改为 /([^a-zA-Z0-9_.,\s])/
文件app/Core/Model.php

4.2 captcha 验证成功未销毁 Session

项内容
风险验证成功后未 unset(),180 秒窗口内可被重放
修复verify() 返回 true 前销毁 Session 标记
文件app/Helpers/Captcha.php

4.3 Tag.php getAll() 全表扫描性能

项内容
风险LEFT JOIN + GROUP BY + ORDER BY count DESC 在大数据量时无法利用索引
修复tags 表冗余 thread_count 计数器字段
文件app/Helpers/Tag.php

4.4 Category.php GROUP BY 性能

项内容
风险每次首页加载都执行全表 COUNT + GROUP BY,百万级数据时数秒返回
修复categories 表冗余 thread_count/post_count 字段
文件app/Models/Category.php

4.5 Theme.php 正则 \r 残留

项内容
修复改为 /:\s*([^\r\n]+)/i 排除回车换行
文件app/Helpers/Theme.php

4.6 登录锁定基于 Session 可被绕过

项内容
修复锁定状态基于用户名存数据库(当前已有验证码作为第二道防线)
文件app/Controllers/AuthController.php

4.7 Controller 开放重定向

项内容
修复redirect() 对 URL 做 parse_url() 检验是否本站域名
文件app/Core/Controller.php

4.8 弱类型比较

多处 == 改为 ===,建议顺手修。

4.9 LOCK_EX 写入锁(PluginController)

Plugin::updateConfig() 写入 plugin.json 时加 LOCK_EX。


执行顺序建议

第一轮(致命 7 项)       → 2 天
  ├── 1.1 ZIP RCE + 孤儿目录
  ├── 1.2 Upload 白名单
  ├── 1.3 Plugin 文件包含
  ├── 1.4 SSRF
  ├── 1.5 User 递归死循环 + 事务
  └── 1.6 Search DDL

第二轮(高危 9 项)       → 1.5 天
  ├── 2.1 二次验证 + 超时
  ├── 2.2 管理员自降级
  ├── 2.3 vote.js CSRF
  ├── 2.4 logout 不退出的 Bug
  ├── 2.5 后台 CSRF 补漏
  ├── 2.6 strpos 鉴权
  ├── 2.7 文件删除安全
  ├── 2.8 数据库导入路径
  └── 2.9 editor.js quote XSS

第三轮(重要 8 项)       → 0.5 天
  ├── 3.1 Setting $qc
  ├── 3.2 Content Base64
  ├── 3.3 Blog decode
  ├── 3.4 Vote IN 子句
  ├── 3.5 LOCK_EX
  ├── 3.6 Points LIMIT
  ├── 3.7 附件清理误伤
  └── 3.8 积分条件竞争(可延后)

第四轮(优化 9 项)       → 顺手修
  4.1 ~ 4.9 规范/性能


涉及文件清单(去重后)

层文件
ControllersAdmin/ThemeController、Admin/UserController、Admin/BaseController、Admin/BlogController、Admin/CategoryController、Admin/DatabaseController、Admin/MaintenanceController、ApiController、AuthController
CoreController、Model
HelpersUpload、Plugin、Settings、Content、Points、Captcha、Search、Tag、Theme
ModelsUser、Setting、Blog、Category
JSassets/js/vote.js、assets/js/editor.js

总计约 20 个文件,每项改动 1-30 行,全部修完预计 4-6 天。


我的想法

  1. 第一轮 6 项必须修 — ZIP RCE、Upload 白名单、Plugin 文件包含、SSRF、User 递归死循环、Search DDL。任何一个被利用都会导致服务器出大问题。
  1. 第二轮 9 项建议修 — 最值得注意的新问题是 管理员自降级绕过(唯一管理员可把自己变为普通用户→后台锁死)和 logout 退不出登录(用户体验级 Bug)。vote.js 缺 CSRF 导致点赞瘫痪也是刚需。
  1. 第三轮建议修 — Setting.php 的 $qc 导致后台设置无法保存、Content.php 的 Base64 盲猜导致正常文章变乱码,这是用户能直接感知的问题。附件清理误伤也挺坑的。
  1. 第四轮可以缓缓 — 性能优化在数据量上来再做;弱类型比较不会产生实际安全问题。
  1. 总体评价 — 修完这批问题后,SoSite 的安全性和稳定性会非常扎实。主要集中在"写了但没中断"、"写了但忘了消费"这类细节疏忽上,没有架构层面的硬伤。
轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 浏览 4 次 | 回复 0 次

全部回复 (0)

暂无回复

请 登录