本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:检查结果.md。
SoSite V3.8.0 代码深度审查报告
审查时间:2026-07-08
审查范围:全部 PHP / CSS / JS 文件(150+ 个文件)
审查维度:安全 / 逻辑 / 冗余 / 关联性
一、总体评估
| 维度 | 评分 | 说明 |
|---|---|---|
| 安全 | 45/100 | 多处高危:数据库弱密码硬编码、模板缓存竞态条件可致代码注入、install.php SQL 注入、CSRF 防护不完整、SSRF 防护可绕过、多插件 AdminController 无权限校验 |
| 逻辑 | 60/100 | $thread 未定义变量导致"楼主删回复"功能完全失效;User::deleteWithCascade 删除顺序错误致关联数据清理失败;Admin/ThemeController::setDefault 条件逻辑反转;权限过滤逻辑跨模块不一致 |
| 冗余 | 55/100 | database/init.php 与 protected/database/init.php 大量重复;Controller 安全头重复;插件 activate/init_after 双重建表;Emoji 双映射表;Helper 与 Model 职责重叠 |
| 关联性 | 50/100 | 版本号不一致(V3.8.0 vs V4.0.0);dev_docs 命名空间大小写不一致(Linux 致命);SQLite 兼容性不完整;路由注册缺少 require_once;分页条数来源不一致 |
综合评分:52/100(中等偏下)
二、高危问题清单(17 项)
H-01 | 数据库弱密码硬编码
- 文件:
config.php:9 - 代码:
define('DB_PASS', '123456'); - 描述:数据库密码硬编码为弱密码
123456,且该文件位于 Web 可访问目录,若服务器配置不当可直接暴露密码 - 修复:使用强密码;将 config.php 移至 Web 根目录外;Web 服务器配置禁止访问
H-02 | 模板缓存竞态条件(可致任意代码执行)
- 文件:
app/Core/TemplateCompiler.php:216-226 - 代码:
file_exists检查后无锁地file_put_contents缓存文件,然后require该文件 - 描述:TOCTOU 竞态条件:并发请求可能 require 到部分写入的缓存文件,导致 PHP 解析错误或代码注入;
file_put_contents非原子操作 - 修复:使用临时文件 +
rename(原子操作)替代直接file_put_contents;或使用flock加文件锁
H-03 | 模板编译 {if} 条件表达式无安全过滤
- 文件:
app/Core/TemplateCompiler.php:155-159 - 代码:
cbIf()和cbElseif()直接将模板变量嵌入 PHP 代码,无安全过滤 - 描述:若主题上传功能允许上传自定义模板,攻击者可构造恶意模板文件注入 PHP 代码
- 修复:对
{if}/{elseif}表达式做语法白名单校验;禁止用户上传自定义模板
H-04 | install.php 数据库名 SQL 注入
- 文件:
protected/database/install.php:106 - 代码:
$pdo->exec("CREATE DATABASE IF NOT EXISTS{$dbname}...") - 描述:
$dbname来自用户 POST 输入,仅经trim()未做格式校验,可构造恶意数据库名逃逸反引号 - 修复:
preg_match('/^[a-zA-Z0-9_]+$/', $dbname)白名单校验
H-05 | install.php DSN 注入
- 文件:
protected/database/install.php:103-104 - 代码:
$dsn = "mysql:host={$host};port={$port}" - 描述:
$host和$port来自用户 POST 输入,直接拼入 DSN 字符串,可注入额外 DSN 参数 - 修复:对
$host做域名/IP 校验;$port做(int)转换并检查 1-65535 范围
H-06 | database/init.php 表名 SQL 拼接
- 文件:
database/init.php:119/protected/database/init.php:98 - 代码:
$stmt = $conn->query("SHOW COLUMNS FROM{$tableName}") - 描述:
$tableName直接拼接进 SQL,addColumnIfNotExists()的$column和$definition同理,与tableExists()的白名单校验不一致 - 修复:对所有动态标识符做正则白名单校验
H-07 | Session use_strict_mode = 0
- 文件:
config.php:33 - 代码:
ini_set('session.use_strict_mode', 0); - 描述:接受客户端提供的任意 session ID,存在 Session Fixation 攻击风险。install.php 生成的新 config.php 设为
1,当前配置不一致 - 修复:改为
ini_set('session.use_strict_mode', 1);
H-08 | settings_cache.php 安全数据明文存储
- 文件:
protected/settings_cache.php:16 - 代码:
'captcha_salt' => '7ad552abb3ca545aa9719885077e1b36' - 描述:验证码 salt 硬编码在缓存文件中且以明文存储;登录锁定信息也持久化到缓存文件
- 修复:salt 从环境变量或独立配置读取;登录锁定状态应存于数据库/Redis
H-09 | ApiController::upload 缺失 CSRF 验证
- 文件:
app/Controllers/ApiController.php:86-121 - 描述:upload 方法完全缺失 CSRF 验证,攻击者可构造恶意页面上传任意文件。同文件中 vote 方法有 CSRF 校验,upload 遗漏
- 修复:添加
Csrf::verifyOrDie($_POST['csrf'] ?? '')
H-10 | ThemeController::settings 三重安全缺陷
- 文件:
app/Controllers/ThemeController.php:16-22 - 描述:(1) 无 CSRF 验证;(2) 无登录检查;(3)
$theme值未与可用主题列表校验,可能导致路径遍历 - 修复:添加
Csrf::verifyOrDie()+requireLogin()+Theme::getAvailable()白名单校验
H-11 | ThreadController::delete $thread 未定义变量
- 文件:
app/Controllers/ThreadController.php:273 - 代码:
$isOp = (int)$thread['user_id'] === (int)$currentUser['id']; - 描述:
$thread在此时未定义(282行才赋值),导致"楼主删回复"功能完全失效 - 修复:在 273 行之前查询 thread 信息
H-12 | Admin/DatabaseController::mysqlImport 可执行任意 SQL
- 文件:
app/Controllers/Admin/DatabaseController.php:62,126-145 - 描述:导入的 SQL 文件被直接
exec()执行,可执行任意 SQL 语句;SQL 解析器极其简陋;catch 块静默吞掉错误 - 修复:增加额外密码确认或独立 CSRF token;使用 mysql 命令行工具替代手工解析;移除空 catch
H-13 | Content.php PHP 标记还原为原始文本
- 文件:
app/Helpers/Content.php:104-117 - 描述:还原 PHP 标记时将占位符还原为
<?php和<?=原始文本而非 HTML 实体,在错误配置下可能触发代码注入 - 修复:PHP 标记也应还原为 HTML 实体
H-14 | Captcha fallback 盐值可预测
- 文件:
app/Helpers/Captcha.php:30-32 - 代码:
return \hash('sha256', 'SoSiteCaptcha_' . \__DIR__); - 描述:fallback 盐值基于
__DIR__和固定字符串,可预测性极高 - 修复:fallback 使用
random_bytes(16)或强制从 settings 表读取
H-15 | Upload Imagick readImageBlob 未限制格式
- 文件:
app/Helpers/Upload.php:122 - 描述:Imagick 的
readImageBlob可处理 SVG/MSL/MVG 等危险格式,可能触发 RCE - 修复:在 Imagick 读取前明确限制格式
H-16 | User::deleteWithCascade 删除顺序错误
- 文件:
app/Models/User.php:230-283 - 描述:先删除 threads/posts 后再删除投票和附件,导致子查询返回空集,投票和附件清理完全失效
- 修复:调整顺序为:投票 -> 附件 -> posts -> threads -> 其他 -> 用户
H-17 | 多插件 AdminController 无权限校验
- 文件:
plugins/announcements/AdminController.php、content_review/AdminController.php、friend_links/AdminController.php、seo/AdminController.php - 描述:多个 AdminController 没有任何管理员权限校验,若路由中间件被绕过,任何登录用户可管理插件
- 修复:所有 AdminController 每个方法开头添加
requireAdmin()
三、中危问题清单(50 项)
核心层(13 项)
- M-01 config.php 版本号 V3.8.0 vs install.php 生成 V4.0.0
- M-02 Session 存储双轨并存(config.php 文件 session vs init.php MySQL session)
- M-03 index.php 插件路由
{name}参数未白名单校验,路径遍历风险 - M-04 API 路由
/api/vote、/api/upload无 CSRF;/api/fetch-imageSSRF 风险 - M-05 Autoloader 路径穿越检查不充分(只过滤
..未过滤绝对路径) - M-06 Controller::view() 每次创建新 TemplateCompiler,忽略全局实例
- M-07 Controller 安全头重复且
viewRaw()缺少 CSP/Permissions-Policy - M-08 Database 连接失败直接 echo + exit,可被 DoS 利用
- M-09 Model::insert 列名未校验,key 含反引号可逃逸
- M-10 Model::buildWhere 允许原始 SQL 条件字符串,绕过参数化查询
- M-11 Router
?r=参数仅阻止 admin 前缀,未阻止其他敏感路径 - M-12 Router 未验证 REQUEST_METHOD 白名单,REQUEST_URI 可能为 null
- M-13 TemplateCompiler detectTheme 每次查数据库 + extract 双重执行
Controller 层(14 项)
- M-14 ApiController SSRF DNS Rebinding 绕过 + DNS 失败时校验跳过
- M-15 AuthController::resendVerification 无速率限制 + GET 触发邮件
- M-16 AuthController::resetPassword tokenValid 逻辑错误致密码重置表单消失
- M-17 BlogController 评论内容未 Content::decode()
- M-18 ProfileController 权限过滤逻辑与 ForumController/HomeController 不一致
- M-19 Admin/SettingsController 数值设置未校验 + SMTP 密码明文存储
- M-20 Admin/ThemeController CSS 注入过滤不充分
- M-21 Admin/ThemeController setDefault 条件逻辑错误(
||应为&&) - M-22 Admin/ThemeController 主题上传空文件名风险
- M-23 Admin/ThreadController edit 缺失 CSRF
- M-24 PostController 异常消息泄露数据库信息
- M-25 ThreadController 颜色值未校验致 CSS 注入
- M-26 Admin/DatabaseController 备份大表内存溢出
- M-27 ThreadController 分页条数来源不一致(常量 vs Settings)
Helper/Model 层(10 项)
- M-28 Csrf Token 全 Session 不变,泄露后可反复使用
- M-29 Content.php
javascript:协议检测可绕过(空白字符 + HTML 实体变体) - M-30 Plugin::executeHook 使用 extract + include,暴露内部变量
- M-31 RateLimiter 基于 Session 易绕过(清 Cookie 即获新配额);CRC32 碰撞
- M-32 RememberMe 用户 ID 存 Cookie 暴露内部结构
- M-33 Category Model 使用 unserialize 反序列化缓存,对象注入风险
- M-34 Search.php 允许
*通配符搜索致全表扫描 DoS - M-35 Thread/User Model LIKE 通配符未转义;User::search 返回含密码哈希等敏感字段
- M-36 Settings::require 缓存文件可执行恶意代码
- M-37 Settings::flushCounters 计数器值与内存缓存可能不同步
插件层(13 项)
- M-38 qa/AdminController 全部 POST 方法无 CSRF
- M-39 seo custom_head 直接 echo 致全站 XSS
- M-40 seo sitemap XML 未转义 $siteUrl
- M-41 content_review 仅前端隐藏非数据库隐藏,可通过 API 访问
- M-42 invite 邀请码无速率限制且明文存储
- M-43 content_review 通知标题含帖子标题未转义
- M-44 content_review reason 字段复用为头像路径
- M-45 red_packet 退回操作无行锁
- M-46 daily_checkin 签到并发竞态
- M-47 invite 积分消耗可自定义无上限
- M-48 red_packet 分配边界缺陷
- M-49 edit_info/daily_checkin 建表逻辑不一致
- M-50 post_favorite/invite 路由注册缺少 require_once
四、高危关联性问题(2 项)
A-01 | dev_docs 命名空间大小写不一致(Linux 致命)
- 文件:
plugins/dev_docs/AdminController.php(Plugin\DevDocs)vsPlugin.php(Plugin\dev_docs) - 描述:Windows 不区分大小写所以正常运行,但部署到 Linux 时会类找不到致命错误
- 修复:统一为
Plugin\DevDocs
A-02 | install.php CSRF/安全缺陷
- 文件:
protected/database/install.php:441-460 - 描述:
delete_installaction 无 CSRF 校验;明文密码存 Session;安装锁仅file_exists检查
五、低危与建议级问题(42 项)
- config.php Session GC 并发 race condition
- index.php
/index.php路由冗余 - init.php 步骤编号重复(三个步骤4)
- init.php Session 过期时间硬编码 30 分钟 vs gc_maxlifetime 24 分钟
- Autoloader 闭包每次重建
- Controller::viewRaw 重复调用 RememberMe::check()
- Database::getDbType 硬编码返回
mysql - Model::update 非法 key 静默跳过不报错
- TemplateCompiler realpath 返回 false 未检查
- database/init.php 与 protected/database/init.php 大量重复代码
- install.php SQL 解析器无法处理含分号的字符串
- install.php CSRF token 未用 json_encode 输出到 JS
- rebuild_runtime.php exec 调用 + 无 HTTP 访问限制
- BlogController 浏览数 POST 时也递增 + cover_image 未用 basename
- ThreadController incrementView 在 POST 时也递增 + $currentUser 重复赋值
- Admin/BaseController rtrim 后冗余条件
- Admin/TrashedController 死变量
- Admin/LevelController 成功消息 redirect 后丢失
- ApiController upload 分支结构冗余
- ForumController/HomeController Controller 层做 HTML 转义(应由 View 负责)
- TagsController 权限过滤在分页后导致每页条数不固定
- MessageController 私信发送无速率限制
- Auth::isAdmin role + group_id 双条件 fallback 值问题
- Auth::requireAdminVerify 仅依赖 Session 无服务端绑定
- Mailer 非加密连接时 AUTH LOGIN 凭据暴露
- Emoji QQ_MAP 和 QQ_LIST 重复数据
- Theme 预览功能无权限检查 + Cookie 缺 secure 属性
- Upload MIME 映射表不覆盖 ALLOWED_EXTENSIONS 全部类型
- Vote toggle() 绕过 Database::query() 直接用 prepare
- Settings::require 缓存文件可执行恶意代码
- Settings::flushCounters 计数器值与内存缓存可能不同步
- Settings::updateOnlineStatus 每次请求都清理过期记录
- Blog::batchDelete 无事务保护
- Thread::batchDelete 未删除缩略图
- Message::deleteOwned 一方删除导致双方都看不到
- Permission getAuthorizedCategoryIds 与 check 重复逻辑
- Search searchMySQLFulltext 与 searchLIKE 结构重复
- Points::award 无事务保护
- friend_links/AdminController 重复的 session 赋值行
- content_review/hook/nav_main_links.php 已废弃但仍注册
- daily_checkin/hook/nav_user_menu_items.php 已废弃但仍注册
- edit_info 两个编辑钩子高度重复代码
六、View 层安全问题摘要
auth/login.php:多处未转义输出存在 XSS($_GET['error']、$_GET['redirect']直接输出)invite/index.php:$inviteCode和$errorMsg未转义layouts/main.php:40:custom_css直接输出<style>标签,CSS 注入风险thread/show.php:多处用户输入直接输出blog/detail.php:评论内容渲染安全性取决于 Content::decode 实现admin/user_edit.php:custom_css字段直接输出
七、CSS/JS 前端问题摘要
CSS 冗余
- 4 套侧边栏样式重复定义(admin.css + forum.css + pages.css + layout.css)
- 10 组样式属性冲突(同一选择器在不同文件中定义不同值)
- 主题 CSS 中大量
!important覆盖(至少 12 处)
JS 安全
script.js:AJAX 请求应统一 CSRF token 处理editor.js:编辑器内容应在前端做基础校验vote.js:投票结果应防重复提交
八、修复计划
阶段一:紧急安全修复(P0 - 立即执行)
| 编号 | 修复项 | 涉及文件 |
|---|---|---|
| H-02 | 模板缓存竞态 -> 临时文件+rename | TemplateCompiler.php |
| H-04 | install.php SQL 注入 -> 白名单校验 | install.php |
| H-05 | install.php DSN 注入 -> 类型校验 | install.php |
| H-07 | session.use_strict_mode = 1 | config.php |
| H-09 | upload CSRF -> 添加 verifyOrDie | ApiController.php |
| H-11 | $thread 未定义 -> 提前查询 | ThreadController.php |
| H-13 | PHP 标记还原 -> HTML 实体 | Content.php |
| H-16 | deleteWithCascade 顺序 -> 重排 | User.php |
| H-17 | AdminController 权限校验 -> requireAdmin | 多个插件文件 |
| A-01 | dev_docs 命名空间 -> 统一大写 | Plugin.php |
阶段二:高危安全加固(P1 - 3天内)
| 编号 | 修复项 | 涉及文件 |
|---|---|---|
| H-01 | 数据库弱密码 -> 强密码+移出Web目录 | config.php |
| H-03 | 模板 {if} 白名单 -> 表达式校验 | TemplateCompiler.php |
| H-06 | 表名 SQL 拼接 -> 白名单校验 | database/init.php |
| H-08 | captcha_salt+login_lock -> 安全存储 | settings_cache.php |
| H-10 | ThemeController 三重缺陷 -> CSRF+登录+白名单 | ThemeController.php |
| H-12 | DatabaseController 导入 -> 额外确认+错误报告 | DatabaseController.php |
| H-14 | Captcha fallback 盐值 -> random_bytes | Captcha.php |
| H-15 | Imagick 格式限制 -> setFormat | Upload.php |
阶段三:中危修复(P2 - 1周内)
- CSRF 遗漏补全(Admin/ThreadController::edit、qa/AdminController、ThemeController::settings)
- SSRF 防护增强(DNS Rebinding、follow_location)
- AuthController::resendVerification -> POST + 速率限制
- AuthController::resetPassword -> tokenValid 逻辑修正
- Admin/ThemeController::setDefault -> 条件修正
- Admin/ThemeController CSS 注入 -> 增强过滤
- Model::insert -> 列名校验
- Router -> ?r= 白名单 + REQUEST_METHOD 校验
- Vote::toggle -> 统一使用 Database::query()
- Thread/User Model LIKE 通配符转义
- User::search -> 不返回敏感字段
- 插件 CSRF 遗漏 + 安全加固
阶段四:逻辑与冗余修复(P3 - 2周内)
- 统一 Session 存储策略(消除双轨)
- 统一分页条数来源(Settings vs 常量)
- 统一权限过滤逻辑(空 allowedIds 的语义)
- Content::decode() 调用统一
- 删除 database/init.php 重复文件
- Controller 安全头提取为公共方法
- Emoji 双映射表合并
- Helper/Model 职责厘清(Setting、Tag)
- 插件建表机制统一
- 清理废弃钩子文件
阶段五:前端优化(P4 - 1个月内)
- View 层 XSS 修复(login.php、invite/index.php、main.php 等)
- CSS 侧边栏样式合并
- CSS !important 清理
- JS CSRF token 统一处理
九、统计数据
| 级别 | 安全 | 逻辑 | 冗余 | 关联性 | 合计 |
|---|---|---|---|---|---|
| 高危 | 14 | 2 | 0 | 1 | 17 |
| 中危 | 22 | 16 | 4 | 8 | 50 |
| 低危 | 6 | 18 | 10 | 4 | 38 |
| 建议 | 2 | 4 | 3 | 1 | 10 |
| 合计 | 44 | 40 | 17 | 14 | 115 |
本报告仅为检查结果和修复计划,不执行任何修改。等主人指令后按计划逐步修复。