本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:合并篇(见正文内 merged_from 列表)。
全盘审计报告与修复计划(0816 / 0818 两轮)
本篇由 4 篇原始文档合并去重而成:两轮审计各含「计划 + 报告」。两轮均为只读审计(未修改任何代码/数据/配置,对拍脚本用后即删),审计人 AtomCode(deepseek-v4-flash)。
一、审计背景与方法
| 项 | 第一轮(08-16) | 第二轮(08-18) |
|---|---|---|
| 依据 | 全盘审计计划六阶段流程 + 三份交接文档(P14/P25/P26) | 十二节框架 + 《PHP代码安全审计完整维度》 |
| 审计范围 | flinthub 程序本体 | 仅程序本体(根目录入口 / app/ / assets/ / cli/ / plugins/ / data/ / protected/);update/ 不审 |
| 基线 | V1.0.0,PHP 8.0.2 / SQLite 3.33.0(无 RETURNING)/ phpstudy + Nginx;用户 50,008 / 版块 7 / 博客 5 | 同左(数据规模复用上轮基线快照) |
| 数据基线 | topic_index 192,111;reply_index 160,753;search_index 4 季度合计 2,357,922 行;当前季度 32 桶(topic 26,282 / reply 21,960) | 同上 |
| 语法 | 全量 php -l app/ 0 错误 | 沿用上轮结论,本轮只读未改动 |
| 镜像一致性 | fc /b 抽查 6 个文件全部逐字节一致 | 未重复 |
二、第一轮(08-16)
2.1 总览
合计:0 严重 / 1 中 / 6 低 —— 无致命问题,核心六维全部达标。中等问题 1 项(缓存键页码未钳制,可被刷缓存文件)。
| 维度 | 结论 | 问题数(严重/中/低) |
|---|---|---|
| 代码安全 | ✅ | 0/1/4 |
| 数据一致性 | ✅ | 0/0/0 |
| 性能 | ✅ | 0/0/1 |
| 兼容性 | ✅ | 0/0/0 |
| 功能回归 | ✅ | 0/0/0 |
| 部署交付 | ✅ | 0/0/1 |
2.2 问题清单(7 项)
| # | 级别 | 维度 | 位置 | 问题 | 修复建议 |
|---|---|---|---|---|---|
| 1 | 中 | 性能/安全 | ForumController.php:35,183,221 | 缓存键页码未钳制到 totalPages:$cachePage = max(1, min(100000, page)) 用于缓存 key,而查询用钳制后的 $page。恶意游客遍历 ?page=1..100000 可生成最多 10 万个缓存文件(累计可达数 GB),磁盘 DoS + 缓存污染 | ✅ 已修复(P27):render 写盘键改用钳制后的 $page;首页缓存键固定 home_1;总量封顶 totalPages |
| 2 | 低 | 代码安全 | Admin/ThreadController.php:87 + Views/admin/threads.php:79 | 帖色 color 字段无格式校验:后台直接入库,视图进 style="color:...";$this->e() 不做 CSS 语法拦截,red; background-image:url(javascript:...) 可注入(仅管理员可设置,账号失陷时放大为 XSS) | 入库前白名单校验 ^#[0-9a-fA-F]{3,8}$ 或预设色名,非法回退空串 |
| 3 | 低 | 代码安全 | app/Helpers/Upload.php:58-61 | finfo 不可用时 MIME 静默降级:仅靠扩展名白名单放行,polyglot 文件在 Nginx 未禁 PHP 执行时有解析风险 | finfo 缺失时图片类拒绝;或部署时禁 uploads 目录 PHP 执行 |
| 4 | 低 | 性能 | app/SplitDB/ViewCounter.php:53-85 | flush() 每请求 shutdown 跑 BEGIN IMMEDIATE + 全量遍历 APCu 键;键堆积时 flush 耗时,且在 main_index 写路径上加写锁 | 可接受(apcu_dec 归零不重复处理);若线上有锁竞争可节流至 30s |
| 5 | 低 | 代码安全 | app/Helpers/RateLimiter.php:153 | remaining() 标 @deprecated 且无调用方,死代码 | 审计后删除(连同私有 buildKey) |
| 6 | 低 | 部署交付 | assets/css/admin.css(.pagination/.page-dots) | 后台分页旧样式死代码:P26 已统一为 mn-pagination,旧类无引用 | 可选清理,保留亦无风险 |
2.3 通过项(第一轮关键验证点)
- 代码安全 C1~C10 全部通过:全站 PDO 参数绑定(IN 子句全部 intval);
grep echo $_GET/POST/REQUEST/COOKIE于app/Views/0 命中;全部 POST 端点逐一核对 CSRF;CSS 注入走Content::sanitizeCssStyle()五属性 + 值字符白名单 + 危险模式拦截;越权防护含后台 30 分钟二次密码验证 +PostController::create二次校验canPost/canAttach;上传 9 类扩展名白名单 + finfo 严格匹配 + 5MB +random_bytes随机名 + >5000px 像素炸弹防护 + Imagick 禁 SVG/MSL/MVG。 - 数据一致性 D1~D8 全部通过(实测):桶 topic 总数 192,111 = topic_index 行数 192,111;reply 160,753 = 160,753;
extern_path空值 = 0;topic_index.bucket_path空/非法 = 0;统计缓存永久化 JSON(防 unserialize RCE)+ 六个写入点主动失效。 - 性能 P1~P8 通过(实测):keyset 深翻页稳态 0.171ms / 0.121ms(首测 15.459ms 为冷 WAL/杀软首触);OFFSET 对照 239,000 行 = 481.695ms(已知慢路径,仅页码数字直达);
getIndexPaginated走复合索引SCAN ... USING INDEX idx_topic_index_list,无全表扫描、无 TEMP B-TREE(P25 覆盖索引误选教训未复发);统计 GROUP BY 78.282ms / 7 分类,缓存命中 ~2.14ms。
三、第二轮(08-18)
3.1 与第一轮的差异
- 范围从"程序本体"进一步明确为六个具体区域,且显式排除
update/。 - 维度从六维扩到十一节(新增:输入源、业务逻辑、会话/Cookie/安全头、文件上传、第三方 & CLI、配置/错误/日志、模板/视图/htmx、社区功能面/备份导出)。
- 重点转向"部署与入口层"——这是与第一轮最大的不同:第一轮没查 Web 服务器层,第二轮发现 3 个高危全在这里。
3.2 总览
| 维度 | 结论 | 问题数(高/中/低) |
|---|---|---|
| 架构与入口 | ⚠️ | 2/1/2 |
| 输入源 | ✅ | 0/0/1 |
| 代码安全 | ✅ | 0/1/1 |
| 业务逻辑 | ✅ | 0/0/0 |
| 数据层 & SplitDB | ✅ | 0/1/0 |
| 会话 / Cookie / 安全头 | ✅ | 0/0/0 |
| 文件上传 | ✅ | 0/1/0 |
| 第三方 & CLI | ⚠️ | 1/1/1 |
| 配置 / 错误 / 日志 | ✅ | 0/1/1 |
| 模板 / 视图 / htmx | ✅ | 0/0/0 |
| 社区功能面 / 备份导出 | ✅ | 0/0/0 |
合计:3 高 / 6 中 / 6 低。结论:可上线,但须先处理 3 个高危项。
3.3 高风险问题(3 项,全部当日修复)
| # | 问题 | 证据 | 修复 |
|---|---|---|---|
| 1(高) | cli/ 目录可 Web 直访执行 | 诊断/冒烟脚本直访会输出内部路径、tokenize 结果、库统计 | Nginx 增加 cli/ deny + plugins/[^/]+/data/ deny(泛化覆盖 16 插件)+ assets/uploads/*.php 禁执行;同时新增 cli/_guard.php(PHP_SAPI !== 'cli' → 403),33 个 cli 脚本在加载框架前 require,probe_page_cache.php 单独插入 |
| 2(高) | 插件 SQLite 库可被下载 | plugins//data/.sqlite 无 Web 防护 | Plugin::db() 自动为插件 data/ 目录补写 .htaccess(Apache deny)+ web.config(IIS hiddenSegments),幂等;存量 15 插件目录用一次性脚本补齐(用完即删) |
| 3(高) | upgrade_p41.php 无鉴权可触发 | 升级脚本 Web 可直访问 | Web 访问要求管理员会话(Auth::isAdmin,异常一律 403),CLI 执行不受限 |
3.4 中等问题(6 项)
| # | 问题 | 修复 |
|---|---|---|
| 4 | cron_trigger.php 无可选触发密钥 | 增加 CRON_KEY 常量校验(非空时 hash_equals 比对 ?key=,留空兼容旧部署) |
| 5 | finfo 不可用时图片类未拒绝 | 图片类(jpg/jpeg/png/gif/webp)拒绝,非图片保留扩展名兜底 |
| 6 | RateLimiter 缓存 unserialize 反序列化面 | 缓存读写改 json_encode/json_decode(旧 serialize 缓存自动失效覆盖) |
| 7 | DBFactory::foreign_keys = OFF 策略未标注 | 决策标注:分片跨库外键不成立 + 业务显式清理已走查确认 + 开启需重排删除顺序 |
| 8 | 反代/CDN 场景 Host 头注入 | SITE_URL 支持环境变量 FLINTHUB_SITE_URL / SITE_URL_OVERRIDE 覆盖 |
| 9 | 数据库备份无限堆积 | 自动轮转:仅保留最近 BACKUP_KEEP=10 份完整备份(realpath 校验后删最旧) |
3.5 低危问题(6 项)与处置
AuditLog::getClientIp 信任 XFF 首 IP(部署标注,不影响鉴权/限流);Permission::canAttach 列缺失时默认放行(改为拒绝,且列检测从 MySQL 语法的 SHOW COLUMNS 改为 SQLite 兼容 PRAGMA table_info——原写法在 SQLite 下恒 false,本身就是隐患);install.php 安装完成后返回 410 Gone 并移除 .install_done 路径提示;{:raw} 原始输出点在 Views 中 0 使用(维持,新增必须过 Content::sanitizeHtml);CSP 含 unsafe-inline/unsafe-eval(取舍标注,htmx/Alpine 运行必需);ViewCounter::flush 改 30s 节流。
3.6 第二轮通过项(与第一轮一致且无回退)
代码安全六维(SQLi/XSS/CSRF/越权/SSRF/命令)全部通过,且上轮新增点复核无回退。核心业务链路防护完备:dummy hash 防枚举、DB 级登录锁定、30 分钟二次验证、SSRF 逐跳校验、zip-slip 防护。业务逻辑 D1~D7、会话/安全头 F1~F4、模板 K1~K3、备份导出 S6 均通过。
四、两轮对比
| 维度 | 第一轮(08-16) | 第二轮(08-18) |
|---|---|---|
| 发现问题 | 0 严重 / 1 中 / 6 低 | 3 高 / 6 中 / 6 低 |
| 风险主战场 | 代码逻辑层(缓存键) | 部署/入口层(cli 直访、插件库可下载、升级脚本无鉴权) |
| 高危数 | 0 | 3(全部为配置/文件放置类,修复成本 <10 分钟) |
| 修复落地 | P27 修复缓存键 | 当日完成 15 项修复(3 高 + 6 中 + 6 低中的低危项),php -l 全过 |
| 共同结论 | 代码安全六维通过,无致命问题 | 同左,且第一轮新增点复核无回退 |
两轮共同暴露的系统性问题:
- "代码层安全、部署层裸奔" —— 程序本体的注入/XSS/越权防护明显高于一般 PHP 社区程序,但 Web 服务器层(Nginx deny、SAPI 守卫、插件数据目录防护)此前没有针对性配置,三个高危全在这里。第二轮补齐了
cli/_guard.php与自动写.htaccess/web.config的机制。 - SQLite / MySQL 语法误用隐患 ——
Permission::canAttach用SHOW COLUMNS在 SQLite 下恒 false,导致权限列检测失效;同类问题提示"从 MySQL 迁移过来的代码需逐处核对方言"。 - "退役表"读路径反复出现 —— 第一轮的缓存键、第二轮的 CLI 与上传,都指向同一类问题:迁移后旧路径没有系统清理机制。这一项在 09-02 全量审计中演变为最严重的附件下载权限绕过(见《2026-09-02 全量代码审计与四批次修复实施报告》)。
五、修复执行情况(第二轮,2026-08-18)
| 项 | 内容 | 结果 |
|---|---|---|
| 1(高) | Nginx deny + cli/_guard.php | nginx -t 通过;cli/ 38 文件 php -l 全过 |
| 2(高) | Plugin::db() 自动写防护文件 | php -l 通过;15 插件 data/ 目录已补齐 |
| 3(高) | upgrade_p41.php 管理员会话校验 | php -l 通过 |
| 4~9(中) | cron 密钥、finfo 拒绝、RateLimiter json、外键标注、SITE_URL 覆盖、备份轮转 | 均 php -l 通过 |
| 10~15(低) | XFF 标注、canAttach 拒绝 + PRAGMA、install.php 410、CSP 取舍标注、flush 30s 节流、诊断脚本随 deny 一并解决 | 均 php -l 通过 |
有意保留的取舍: CSP unsafe-inline/unsafe-eval(htmx/Alpine 必需,XSS 由服务端转义兜底);前置静态缓存仅游客命中;AuditLog XFF 仅影响日志展示。