本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:8月13日备份/审计报告-20260812.md。
SoSite 安全与逻辑审计报告(2026-08-12)
审计方式:全代码只读遍历(静态扫描 + 人工走读 + 调用链追踪),交叉验证 2026-08-06 审计报告整改情况。
范围:
app/(Controllers/Core/Helpers/Models/Views)、plugins/(16 个插件)、assets/、protected/、config.php、根目录入口。结论:本次审计未修改任何代码,以下为发现与建议,供决策参考。
一、安全审计(A1-A11)
1.1 SQL 注入 —— 总体良好,无新增可利用点 ✅
参数化纪律继续保持:Database::query() 一律 prepare() + 参数绑定,ATTR_EMULATE_PREPARES=false;IN (...) 全部 intval 过滤或 ? 占位符(Thread::batchDelete / User::deleteWithContent / Vote::getCounts);LIMIT/OFFSET 显式 (int);Model::buildWhere 正则白名单。未发现用户输入直拼 SQL 的注入点。
| # | 位置 | 风险 | 说明 |
|---|---|---|---|
| S1 | app/Controllers/Admin/DatabaseController.php mysqlImport | 🟠 中 | 管理员导入 .sql 逐条 $conn->exec() 原样执行(功能固有风险,CSRF+路径+扩展名校验在,但被篡改备份可致全库沦陷)——2026-08-06 已报,未变 |
| S2 | app/Core/Model.php:198-201 | 🟡 低 | sanitizeOrderBy 黑名单剔除而非白名单——已报,未变 |
1.2 XSS 输出 —— 转义纪律良好 ✅
- 模板引擎
{$var}默认htmlspecialchars转义;{$var:raw}为显式原始输出 ✅ - 视图扫描:132 处输出初筛 → 全部经
$this->e()/(int)/ 模板转义;勋章插件视图(admin.php / index.php)均$this->e()转义 ✅ Content::sanitizeHtml白名单净化完整:允许标签/属性白名单、on*属性删除、href/src 协议白名单(含 java script: 空格绕过防御)、SVG 拒绝、<?php占位符保护 ✅Content::formatPostContent纯文本 → htmlspecialchars + 自动链接转义 ✅- X1 已修复 ✅:
theme/settings.php的$color已$this->e()转义(2026-08-06 报 X1) - X2(search_settings.php
$percent)未强转,但为纯数值(round()),语义安全,🟡 建议(int)兜底
1.3 CSRF —— 覆盖完整 ✅
所有写操作均 Csrf::verifyOrDie() / Csrf::verify():发帖/回帖/编辑/删除/私信/上传/投票/收藏/签到/骰子/红包/勋章佩戴/邀请码/友链申请/关注、全部 15 个 Admin 控制器(继承 Admin/BaseController)+ 6 个插件 Admin 控制器。Csrf::verify 只校验不轮换(防并发 AJAX 403),登录/登出 rotate() 显式轮换 ✅。未发现缺失。
1.4 文件上传 —— 校验完善,服务器防护仍缺失 ⚠️
Upload.php 链路完整:扩展名白名单 → is_uploaded_file → finfo MIME 严格匹配 → 16 字节随机名 → move_uploaded_file → 缩略图(>5000px 像素炸弹拒绝、Imagick 格式限制、SVG 禁用)✅
| # | 位置 | 风险 | 说明 |
|---|---|---|---|
| U1 | assets/uploads/(无防护文件) | 🟠 中 | 无 .htaccess/nginx 禁止脚本执行规则——已报,未修复。虽 MIME+扩展名双白名单无 php,但建议部署 php_flag engine off / nginx location ~ \.php$ { deny all; } |
| U2 | app/Helpers/Upload.php:58-61 | 🟡 低 | finfo 不可用时仅扩展名兜底放行(有 error_log)——已报,未变 |
| U3 | app/Helpers/Upload.php:107 | 🟡 低 | 原图保存前无 getimagesize 真实性校验(仅缩略图阶段有)——已报,未变 |
ApiController::upload:登录 + CSRF + 限流(5/分)✅;AttachmentController::download:basename + realpath 双重校验 + 版块权限(can_view/can_attach)+ attachment_download_before 钩子 ✅
1.5 SSRF —— 两个高危绕过点仍未修复 🔴
ApiController::fetchImage(L158-293)已有:登录 + 限流(20/分)+ URL 协议校验 + host 内网检测 + DNS 解析真实 IP 校验 + IP 替换 host 防 DNS Rebinding。
| # | 位置 | 风险 | 说明 |
|---|---|---|---|
| R1 | L181-205 | 🔴 高 | 169.254.0.0/16(云元数据 169.254.169.254)与 100.64.0.0/10(CGNAT)仍未封禁——已报,未修复 |
| R2 | L217 / L236 | 🔴 高 | follow_location=1 + cURL FOLLOWLOCATION 跟随重定向,重定向目标未逐跳二次内网校验——已报,未修复 |
| R3 | L222-226 | 🟡 低 | SSL 校验关闭(IP 直连所需,可接受但应知悉);端口未限制 80/443 |
1.6 命令注入 —— 零发现 ✅
全库扫描 shell_exec/system/exec/passthru/proc_open/popen:仅注释提及,无实际调用。插件缓存文件改为 json_decode 读取(消除 include RCE 风险)✅。
1.7 认证与会话 —— 配置严谨 ✅
- Session:
use_only_cookies、cookie_httponly、use_strict_mode、samesite=Lax、cookie_secure按 HTTPS 自适应;MySQL sessions 表存储(独立 PDO 连接防游标冲突),表不存在回退文件存储 ✅ - RememberMe:token 仅存 sha256 哈希 +
hash_equals恒定比较 + 自动登录轮换 token + httponly/samesite ✅ - 登录:CSRF + 验证码 + DB 持久化锁定(5 次/15 分钟,换 Session 无法绕过)+
session_regenerate_id(true)+Csrf::rotate()✅ - 注册:限流(3/时)+ 验证码 + CSRF ✅;忘记密码:限流(3/时)+ 防枚举提示 + token 仅存哈希 + 1 小时过期 + 使用后作废 + 重置后清 remember_token ✅
Auth::requireAdmin:30 分钟二次验证(admin_verified_at)✅
| # | 位置 | 风险 | 说明 |
|---|---|---|---|
| P-2 | ProfileController.php:31-52 | 🟠 中 | 改邮箱无需当前密码确认 → 配合忘记密码构成账号接管链(会话窃取 → 改绑邮箱 → 重置密码)——已报,未修复 |
| P-3 | AuthController.php:23-132 | 🟠 中 | 登录仅按用户名锁定,无 IP 级限流,密码喷洒(每账号 1-4 次)不受限;DB 锁定可被恶意触发登录 DoS——已报,未修复 |
| P-6 | AuthController.php:247-312 | 🟡 低 | 忘记密码无验证码(登录/注册有),仅限流——已报,未变 |
1.8 越权(IDOR)—— 总体覆盖良好 ✅
- 私信:
MessageController::detail校验收发双方、markAsRead/deleteOwned带属主条件 ✅ - 主题/回复:
ThreadController::edit/delete属主校验 + 版块权限 + post_id 跨主题校验 + 复合鉴权(楼主可删违规回复)✅ - 附件删除:
PostController::edit/ThreadController::edit删除附件前校验thread_id/post_id归属 ✅ - 博客:
BlogController::create/edit/delete属主校验 ✅ - 勋章:
setWearing校验只能佩戴自己已获得的勋章 ✅
| # | 位置 | 风险 | 说明 |
|---|---|---|---|
| P-5 | BlogController.php:168-278 | 🟠 中 | 前台 Blog 增删改(admin-only)只查 isAdmin(),绕过 admin_verified_at 30 分钟二次验证——已报,未修复 |
| P-9 | ThreadController.php:202/290、PostController.php:172 | 🟡 低 | 管理员前台编辑/删除他人帖子不要求二次验证(与 P-5 同类)——已报,未变 |
| P-8 | ThemeController.php:19-24 | 🟡 低 | 主题切换 POST 无 requireLogin(仅 CSRF),影响面小——已报,未变 |
| P-1 | User.php:38 + SearchController.php:66 | 🟠 中 | 用户搜索仍泄露 email/role/status 给任意登录用户——已报,未修复 |
1.9 信息泄露 —— 少量低危
User::search泄露 email/role/status(见 P-1)- 错误信息不回显用户(
PostController::create捕获异常后提示"发帖失败,请稍后重试")✅ protected/有.htaccess全拒;app/Views/errors/404.php已存在(4.2 悬空引用已修复 ✅)user_profile主页对非本人隐藏 email/email_verified ✅
1.10 配置与部署安全 ⚠️
| # | 位置 | 风险 | 说明 |
|---|---|---|---|
| C1 | config.php:14-15 | 🟠 中 | DB 账号 fastbb/123456 弱默认口令——正式部署必须更换 |
| C2 | config.php:27 | 🟡 低 | MAIL_PASS_KEY 固定 AES 密钥——正式部署必须更换 |
| C3 | assets/uploads/ | 🟠 中 | 同 U1,无脚本执行防护 |
| — | SITE_URL | ✅ | 邮件链接用 SERVER_NAME(防 Host 头注入)✅ |
1.11 插件安全 —— 权限与钩子均规范 ✅
- 6 个插件 Admin 控制器全部继承
Admin\BaseController(含二次验证)✅ - 前台写操作全部
requireLogin+ CSRF:签到/骰子/猜题对决/邀请码/勋章佩戴/通知/收藏/红包/友链申请/关注 ✅ Plugin::hook:路径 realpath + 前缀校验(防穿越)、单钩子 try/catch 隔离、插件缓存 json 化 ✅Plugin::activate建表幂等;缓存文件守卫模式 ✅
二、逻辑审计(B1-B10)
2.1 事务与一致性 —— 核心链路良好,发现 2 处中危缺陷
✅ 良好实践确认:
Points::deduct:事务 +FOR UPDATE行锁,防并发透支 ✅Vote::toggle:ON DUPLICATE KEY UPDATE原子 upsert,防并发重复投票 ✅red_packet:grab/create 均事务 +FOR UPDATE行锁 + 防重复领取 ✅Thread::batchDelete:事务 + 附件名白名单 + 全部intval✅User::deleteWithContent:事务逆序清理(投票→标签→附件→回帖→主题→消息→博客→在线→用户)✅PostController::create:事务包裹(插入主题 + 标签 + 发帖数 + 积分 + 计数)✅
| # | 位置 | 风险 | 说明 |
|---|---|---|---|
| L1 | plugins/quiz_duel/QuizDuelController.php:204-296 | 🟠 中 | accept() 并发缺陷:Points::deduct 先执行(自开事务提交),随后 UPDATE quiz_duels + 发奖无事务包裹;且 accept 前仅 SELECT ... WHERE status='open',无行锁/无条件更新。两个对手可同时 accept 同一场对决 → 双方扣分但只一方发奖,或重复发奖。需 FOR UPDATE 或 UPDATE ... WHERE status='open' 条件更新 |
| L2 | plugins/daily_checkin/CheckinController.php:191-208 | 🟡 低 | 签到 INSERT daily_checkin 与 UPDATE users points 两查询无事务包裹;表有 UNIQUE(user_id, checkin_date) 防重(✅ 防并发双签),但 INSERT 成功而 UPDATE 失败会积分丢失;并发双击时第二次 INSERT 抛异常被 catch 吞掉显示"签到失败" |
| L3 | plugins/invite/InviteController.php:47 | 🟠 中 | 生成邀请码不扣积分:Points::award($userId, -$pointsCost, ...) 传负值,而 Points::award 首行 if ($points <= 0) return; 拒绝 → 扣分静默失效(插件当前停用,但逻辑 bug 存在) |
| L4 | plugins/medal/Plugin.php:211-225 | 🟡 低 | grant() 先 SELECT 再 INSERT,无 try/catch 捕获唯一键冲突;并发自动颁发时第二次 INSERT 抛异常会中断 checkAutoRules 循环(user_medals 有唯一键,防重 ✅,但异常未捕获) |
| L5 | plugins/dice/DiceController.php:73-79 | 🟡 低 | 日次数检查(getDailyPlayCount)与扣分(Points::deduct)非原子,并发可少量突破日上限;deduct 行锁正确但 count 检查在锁外 |
2.2 频率限制与防刷 —— 发帖仍无限流 🔴
| # | 位置 | 风险 | 说明 |
|---|---|---|---|
| P-4 | PostController::create:20-170 | 🟠 中 | 发新主题无频率限制(回帖/评论/私信/上传/投票/签到/搜索都有限流,唯独发帖没有)→ 可无限刷帖刷积分(+5/帖)——已报,未修复 |
| P-7 | app/Helpers/RateLimiter.php:97-106 | 🟡 低 | IP 文件缓存读-改-写无锁(仅写时 LOCK_EX),并发可少量突破阈值(TOCTOU)——已报,未变 |
2.3 核心业务(积分/勋章/投票/收藏)—— 设计良好 ✅
- 积分:award/deduct 调用方权限校验完整、防负值、日志完整(points_log)✅
- 投票:
ApiController::vote登录+CSRF+限流+类型白名单+ID 数字校验+获赞积分(自赞不奖)✅ - 勋章:
checkAutoRules固定 3 次查询 + 会话守卫 + 30 分钟节流(性能与正确性兼顾);setWearing事务 + 属主校验 + WEAR_LIMIT ✅ - 收藏:
post_favorite登录 + CSRF + 唯一键防重 ✅ - 签到:
UNIQUE(user_id, checkin_date)防重 ✅;积分封顶 50 ✅ - 红包:grab 事务 + 行锁 + 防重复 + 过期退回 ✅
- 猜题对决:store 扣分失败退还 ✅;accept 并发缺陷见 L1
2.4 消息/通知 —— 属主校验完整 ✅
- 私信收发/已读/删除:全部带收发双方校验(
Message::markAsRead/deleteOwned)✅ - 通知:
NotificationController/content_review::FrontController批量删除带 userId 作用域 ✅
2.5 附件生命周期 —— 防误删设计良好 ✅
cleanupOrphanAttachments:保护头像/封面/内容嵌入图/缩略图 + 24 小时时间缓冲 + 跳过符号链接 + 仅扫 uploads(medals 图放 assets/medals/ 不会被误删,与交接文档约定一致)✅- 删除帖子:附件文件名白名单正则(hex名.扩展名)防路径穿越 ✅
2.6 搜索/分页/排序 —— 参数化与权限过滤 ✅
Search::search:分词 token 全部命名占位符 + 版块权限过滤(allowedCategoryIds)✅User::search:LIKE 通配符转义 + 分页(int)✅sanitizeOrderBy黑名单(S2,低危)
2.7 错误处理 —— 良好 ✅
- 404 视图已存在(errors/404.php);插件 hook 异常隔离;DB 异常统一
error_log不回显 ✅ - 孤儿文件复核:
admin/settings.php、schema_mysql.sql/schema_sqlite.sql、rebuild_runtime.php、emails/verify_email.txt、tmp_audit_xss.php均已删除 ✅
三、2026-08-06 报告整改状态汇总
| 编号 | 问题 | 状态 |
|---|---|---|
| R1/R2 | SSRF 两个绕过点 | ❌ 未修复(🔴 优先) |
| P-1 | 用户搜索泄露 email/role/status | ❌ 未修复 |
| P-2 | 改邮箱无需密码(账号接管链) | ❌ 未修复 |
| P-3 | 登录无 IP 级限流 | ❌ 未修复 |
| P-4 | 发新主题无频率限制 | ❌ 未修复 |
| P-5/P-9 | 前台 admin 操作绕过二次验证 | ❌ 未修复 |
| P-6 | 忘记密码无验证码 | ❌ 未变(低危) |
| P-7 | RateLimiter TOCTOU | ❌ 未变(低危) |
| P-8 | 主题切换无 requireLogin | ❌ 未变(低危) |
| U1 | uploads 无脚本执行防护 | ❌ 未修复 |
| U2/U3 | 上传 finfo 兜底 / 原图未校验 | ❌ 未变(低危) |
| S1 | 后台 SQL 导入 exec | ❌ 未变(功能固有风险) |
| S2 | sanitizeOrderBy 黑名单 | ❌ 未变(低危) |
| X1 | theme/settings.php $color 未转义 | ✅ 已修复 |
| 4.2 | user_profile 悬空 404 视图 | ✅ 已修复(errors/404.php 已建) |
| 4.1 | 孤儿文件(settings.php/schema/rebuild/emails) | ✅ 已删除 |
| 4.5 | 临时文件 tmp_audit_xss.php | ✅ 已删除 |
| C1/C2 | config 弱口令/固定密钥 | ⚠️ 部署注意项(未改) |
四、本次新增发现(2026-08-12)
| # | 位置 | 风险 | 说明 |
|---|---|---|---|
| L1 | quiz_duel accept() | 🟠 中 | 并发应战缺陷(双扣分/重复发奖),需行锁或条件 UPDATE |
| L3 | invite 生成邀请码 | 🟠 中 | Points::award 负值被拒 → 扣分静默失效(插件停用中) |
| L2 | daily_checkin | 🟡 低 | INSERT+UPDATE 无事务,部分失败积分丢失 |
| L4 | medal grant() | 🟡 低 | 唯一键冲突异常未捕获(自动颁发循环中断) |
| L5 | dice 日次数 | 🟡 低 | 次数检查与扣分非原子,并发可突破日上限 |
五、风险汇总矩阵(按优先级)
| 优先级 | 问题 | 位置 |
|---|---|---|
| 🔴 高 | SSRF:169.254.0.0/16 与 100.64.0.0/10 未封禁 + 重定向未二次校验 | ApiController.php:181-236 |
| 🟠 中 | 改邮箱无需密码(账号接管链) | ProfileController.php:31-52 |
| 🟠 中 | 发新主题无频率限制(刷积分) | PostController.php:20 |
| 🟠 中 | 登录无 IP 级限流 | AuthController.php:23 |
| 🟠 中 | 前台 admin-only 操作绕过二次验证 | BlogController / ThreadController / PostController |
| 🟠 中 | 用户搜索泄露 email/role/status | User.php:38 |
| 🟠 中 | uploads 目录无禁止脚本执行规则 | assets/uploads/ |
| 🟠 中 | quiz_duel 并发应战缺陷 | QuizDuelController.php:204 |
| 🟠 中 | invite 生成邀请码不扣积分(停用中) | InviteController.php:47 |
| 🟡 低 | RateLimiter TOCTOU / 忘记密码无验证码 / 主题切换无登录 / sanitizeOrderBy 黑名单 / 上传兜底 / 签到事务 / medal grant 异常 / dice 日次数等 | 多处 |
六、建议整改顺序
- 安全:修复 SSRF 两绕过点(R1:补 169.254.0.0/16、100.64.0.0/10;R2:重定向逐跳校验)→ 改邮箱加密码确认(P-2)→ 发帖补限流(P-4)→ 登录补 IP 级限流(P-3)→ uploads 禁止脚本执行(U1)
- 逻辑:quiz_duel accept 加
FOR UPDATE/条件 UPDATE(L1)→ invite 改用Points::deduct(L3) - 信息泄露:
User::search裁剪字段(P-1) - 低危:签到事务化(L2)、medal grant 捕获唯一键异常(L4)、dice 次数原子化(L5)、RateLimiter 加读锁
- 部署卫生:更换 config.php 弱口令与固定密钥(C1/C2)
审计完成 — SoSite 2026-08-12。本次未修改任何代码,所有结论基于只读检查。