文档目录
文件上传安全

简介

本指南面向 DouPHP 项目的开发者与维护者,聚焦“文件上传安全”的落地实践。内容覆盖:

  • 文件类型白名单、文件大小限制、路径遍历防护的配置与实现
  • 安全处理用户上传文件的流程(验证、恶意检测、存储隔离)
  • 图片处理的安全配置(尺寸、质量、水印、缩略图)
  • 临时文件清理机制与权限控制
  • 常见上传漏洞防护方案与应急响应措施

项目结构

DouPHP 的文件上传能力由“控制器 + 领域服务 + 文件系统抽象 + 校验器”共同构成:

  • 控制器负责接收请求、参数校验、调用附件服务
  • 附件服务负责上传策略、白名单、大小限制、落盘、元数据写入、草稿与认领、缩略图与水印
  • 文件系统提供磁盘抽象、路径规范化、随机文件名与扩展名清洗
  • 校验器提供声明式规则(mimes、max_kb、image/dimensions)用于前置校验
graph TB
A["前端/小程序"] --> B["后台控制器<br/>FileController"]
B --> C["附件服务<br/>AttachmentService"]
C --> D["文件系统抽象<br/>Disk / FilesystemManager"]
C --> E["图片处理<br/>ImageManager"]
C --> F["数据库<br/>dou_file 表"]
B --> G["校验器<br/>Validator"]

图示来源

  • admin/controller/file/FileController.php:48-122
  • core/service/attachment/AttachmentService.php:123-260
  • core/filesystem/Disk.php:92-153
  • core/web/validation/Validator.php:504-560

章节来源

  • config/file.php:43-60
  • admin/controller/file/FileController.php:48-122
  • core/service/attachment/AttachmentService.php:123-260
  • core/filesystem/Disk.php:92-153
  • core/web/validation/Validator.php:504-560

核心组件

  • 配置文件:定义默认磁盘、上传上限、允许扩展名、图片质量等
  • 附件服务:统一封装上传、校验、落盘、元数据、草稿、认领、缩略图、水印
  • 控制器:后台上传入口,支持富文本多图、大文件分块、裁剪替换
  • 文件系统:路径规范化、随机文件名、扩展名清洗、目录创建与移动
  • 校验器:声明式规则(mimes、max_kb、image、dimensions),统一错误消息

章节来源

  • config/file.php:43-60
  • core/service/attachment/AttachmentService.php:123-260
  • admin/controller/file/FileController.php:48-122
  • core/filesystem/Disk.php:92-153
  • core/web/validation/Validator.php:504-560

架构总览

下图展示一次典型“富文本图片上传”的请求链路,体现白名单、大小限制、落盘、缩略图与元数据写入。

sequenceDiagram
participant U as "用户"
participant FC as "后台控制器<br/>FileController.box"
participant AS as "附件服务<br/>AttachmentService.store"
participant FS as "文件系统<br/>Disk"
participant IM as "图片处理<br/>ImageManager"
participant DB as "数据库<br/>dou_file"
U->>FC : 提交表单(含多张图片)
FC->>AS : store(module, item_id, file, type, options)
AS->>AS : 解析白名单/大小/质量/缩略图目录
AS->>FS : move(目标目录+随机文件名)
FS-->>AS : 成功/失败
AS->>IM : resize/watermark/thumb(可选)
IM-->>AS : 处理结果
AS->>DB : insert/update(dou_file)
AS-->>FC : 返回 file number
FC-->>U : 渲染已上传图片列表

图示来源

  • admin/controller/file/FileController.php:48-122
  • core/service/attachment/AttachmentService.php:123-260
  • core/filesystem/Disk.php:92-153

详细组件分析

文件类型白名单与大小限制

  • 默认白名单:jpg/jpeg/gif/png/webp/ico;默认不含 svg,避免存储型 XSS
  • 单文件上限:默认 2048 KB,可通过磁盘配置或选项覆盖
  • 校验点:
    • 附件服务在落盘前进行扩展名校验与大小校验
    • 校验器支持 mimes、max_kb、image、dimensions 等规则,可在控制器层做前置校验
flowchart TD
Start(["开始"]) --> CheckExt["检查扩展名是否在白名单"]
CheckExt --> |否| RejectExt["拒绝:不支持的扩展名"]
CheckExt --> |是| CheckSize["检查文件大小是否超过上限"]
CheckSize --> |否| RejectSize["拒绝:超出大小限制"]
CheckSize --> |是| Move["移动到目标目录(随机文件名)"]
Move --> Done(["结束"])

图示来源

  • core/service/attachment/AttachmentService.php:183-188
  • core/service/attachment/AttachmentService.php:1010-1032
  • core/web/validation/Validator.php:527-551

章节来源

  • config/file.php:43-60
  • core/service/attachment/AttachmentService.php:183-188
  • core/service/attachment/AttachmentService.php:1010-1032
  • core/web/validation/Validator.php:527-551

路径遍历攻击防护

  • 使用 PathNormalizer 拼接目录与文件名,防止穿越
  • Disk 对异常进行安全捕获,非法路径直接返回失败
  • 随机文件名 + 扩展名清洗,避免可控命名导致的路径问题
classDiagram
class Disk {
+putFile(directory, file) string|false
+putFileAs(directory, file, name) string|false
-safeCall(fn, fallback) mixed
-sanitizeExtension(ext) string
}
class PathNormalizer {
+joinDirectory(dir, name) string|false
}
Disk --> PathNormalizer : "路径规范化"

图示来源

  • core/filesystem/Disk.php:92-153
  • core/filesystem/Disk.php:385-399

章节来源

  • core/filesystem/Disk.php:62-76
  • core/filesystem/Disk.php:92-153
  • core/filesystem/Disk.php:385-399

图片处理的安全配置

  • 限宽:可配置 image_width/editor_image_width,避免过大图片占用资源
  • 质量:image_quality/site.quality,默认 90,可按磁盘覆盖
  • 水印:可开启站点级水印,统一注入到主图与缩略图
  • 缩略图:按配置的宽高生成缩略图,并记录 thumb_size
flowchart TD
ImgIn["输入图片"] --> Resize["按配置限宽缩放"]
Resize --> Watermark{"是否启用水印?"}
Watermark --> |是| ApplyWM["应用水印"]
Watermark --> |否| ThumbGen["生成缩略图"]
ApplyWM --> ThumbGen
ThumbGen --> Meta["记录 size/thumb_size"]
Meta --> Done(["完成"])

图示来源

  • core/service/attachment/AttachmentService.php:207-227
  • core/service/attachment/AttachmentService.php:1041-1053
  • admin/controller/file/FileController.php:48-122

章节来源

  • core/service/attachment/AttachmentService.php:207-227
  • core/service/attachment/AttachmentService.php:1041-1053
  • admin/controller/file/FileController.php:48-122

临时文件清理与草稿机制

  • 草稿上传:storeDraft/storeDraftContentImages 写入 status='draft',带 draft_token 与过期时间
  • 草稿认领:claimByToken 将草稿行关联到真实业务主键,并更新状态为 owned
  • 懒 GC:cleanupUserDrafts 清理过期草稿(物理文件 + 缩略图 + 数据库行)
sequenceDiagram
participant U as "用户"
participant FC as "控制器"
participant AS as "附件服务"
participant DB as "数据库"
U->>FC : 提交草稿(含 draft_token)
FC->>AS : storeDraft(..., draft_token)
AS->>DB : 插入 dou_file(status=draft, draft_expire_at)
Note over AS,DB : 等待业务表 INSERT 后认领
FC->>AS : claimByToken(module, draft_token, item_id, kind, id)
AS->>DB : 更新 item_id/status=owned
AS->>AS : cleanupUserDrafts(kind, id, module) 定期清理过期草稿

图示来源

  • core/service/attachment/AttachmentService.php:608-703
  • core/service/attachment/AttachmentService.php:752-776

章节来源

  • core/service/attachment/AttachmentService.php:608-703
  • core/service/attachment/AttachmentService.php:752-776

权限控制与会话绑定

  • 上传身份:通过 withUploader('admin'|'user'|'work', id) 显式声明上传者身份
  • 草稿 token:newDraftToken 生成与身份绑定的 32 位 token,防猜测与越权认领
  • 控制器中强制要求 admin 登录态,确保后台上传受控
flowchart TD
Auth["认证中间件"] --> Kind["确定 uploader_kind(admin/user/work)"]
Kind --> Token["生成/校验 draft_token"]
Token --> Store["store/storeDraft 写入 dou_file(uploader_type/id)"]
Store --> Claim["claimByToken 校验 module+token+kind+id"]
Claim --> Owned["status=owned 完成归属"]

图示来源

  • core/service/attachment/AttachmentService.php:802-831
  • core/service/attachment/AttachmentService.php:778-791
  • admin/controller/file/FileController.php:62-122

章节来源

  • core/service/attachment/AttachmentService.php:802-831
  • core/service/attachment/AttachmentService.php:778-791
  • admin/controller/file/FileController.php:62-122

大文件分块上传与前端校验

  • 前端先调用 act=ext 接口校验扩展名,通过后分片上传
  • 后端 bigfile 动作根据 draft_token 或 item_id 选择草稿/正式上传
  • 分块组装完成后统一走 chunkedStore/chunkedStoreDraft
sequenceDiagram
participant FE as "前端JS"
participant FC as "FileController.bigfile"
participant AS as "AttachmentService"
FE->>FC : POST act=ext(check_filename)
FC-->>FE : 允许/拒绝
FE->>FC : 分片上传(blob_num/total_blob_num/file)
FC->>AS : chunkedStore 或 chunkedStoreDraft
AS-->>FC : 返回进度/结果
FC-->>FE : 进度/完成

图示来源

  • admin/view/js/common.js:1452-1504
  • admin/controller/file/FileController.php:202-264
  • core/service/attachment/AttachmentService.php:492-532

章节来源

  • admin/view/js/common.js:1452-1504
  • admin/controller/file/FileController.php:202-264
  • core/service/attachment/AttachmentService.php:492-532

依赖关系分析

  • 控制器依赖附件服务与 Storage,不直接操作文件系统
  • 附件服务依赖文件系统抽象、图片处理、数据库仓库
  • 校验器独立于附件服务,可在控制器层做前置校验
  • 配置文件集中管理磁盘与上传策略,便于统一治理
graph LR
Config["config/file.php"] --> FS["FilesystemManager"]
FS --> Disk["Disk"]
Controller["FileController"] --> Service["AttachmentService"]
Service --> Disk
Service --> Repo["AttachmentRepository"]
Service --> Image["ImageManager"]
Controller --> Validator["Validator"]

图示来源

  • config/file.php:43-60
  • admin/controller/file/FileController.php:48-122
  • core/service/attachment/AttachmentService.php:123-260
  • core/web/validation/Validator.php:504-560

章节来源

  • config/file.php:43-60
  • admin/controller/file/FileController.php:48-122
  • core/service/attachment/AttachmentService.php:123-260
  • core/web/validation/Validator.php:504-560

性能与安全注意事项

  • 白名单最小化:仅开放必要扩展名,默认不含 svg,降低 XSS 风险
  • 大小限制:结合 PHP 上传限制与系统配置,避免超大文件耗尽内存
  • 图片处理:合理设置 quality 与缩略图尺寸,减少带宽与存储压力
  • 路径安全:始终通过 PathNormalizer 与 Disk 的随机文件名机制落盘
  • 权限隔离:严格绑定 uploader_type/id,草稿 token 与身份强绑定
  • 临时文件:及时清理草稿与缩略图,避免磁盘膨胀
  • 日志与审计:记录上传行为(模块、身份、大小、结果),便于追踪

故障排查指南

  • 上传失败提示“不支持的扩展名”:检查白名单与 mimes 规则
  • 上传失败提示“超出大小限制”:检查 max_kb 与 PHP upload_max_filesize
  • 无法写入目录:检查目录权限与 FileHelper::permission 判定
  • 图片未生成缩略图:检查缩略图尺寸配置与图片处理库可用性
  • 草稿未认领:确认 draft_token 与 identityKind/identityId 一致,且调用 claimByToken
  • 大文件上传中断:检查分片参数与网络稳定性,必要时调整分片大小

章节来源

  • core/service/attachment/AttachmentService.php:183-188
  • core/service/attachment/AttachmentService.php:1024-1032
  • admin/controller/file/FileController.php:130-158
  • core/service/attachment/AttachmentService.php:752-776

结论

DouPHP 的文件上传体系通过“配置驱动 + 服务化封装 + 文件系统抽象 + 声明式校验”,实现了类型白名单、大小限制、路径安全、图片处理、权限隔离与草稿清理的全链路安全。建议在生产环境中:

  • 严格限定白名单与大小限制
  • 启用图片压缩与缩略图
  • 使用草稿与认领机制提升用户体验与安全性
  • 定期清理草稿与无用文件
  • 结合日志与监控快速定位问题

附录:配置与实现清单

  • 白名单与大小限制
    • 默认白名单与默认大小:见配置文件
    • 按磁盘覆盖:在 disks.{name} 中设置 allow_extensions 与 upload_max_kb
  • 图片处理
    • 质量:site.quality 或磁盘 image_quality
    • 缩略图:thumb_directory 与缩略图尺寸
  • 权限与草稿
    • 上传者身份:withUploader(kind, id)
    • 草稿:storeDraft/storeDraftContentImages + claimByToken + cleanupUserDrafts
  • 控制器入口
    • 富文本多图:box
    • 大文件分块:bigfile
    • 图片编辑替换:crop

章节来源

  • config/file.php:43-60
  • core/service/attachment/AttachmentService.php:1010-1053
  • admin/controller/file/FileController.php:48-122
  • admin/controller/file/FileController.php:202-264
  • core/service/attachment/AttachmentService.php:608-703
  • core/service/attachment/AttachmentService.php:752-776
添加日期:2026-10-05