Beego上传时须用io.ReadFull读前512字节,再经http.DetectContentType检测真实MIME,比对白名单;不可信fileheader.Header.Get("Content-Type")或扩展名,校验后须file.Seek(0, 0)重置指针。

Beego 上传时如何获取并校验原始 MIME 类型
Beego 的 multipart.FileHeader 提供了 Header.Get("Content-Type"),但这个值完全由客户端提交,不可信。真正可靠的 MIME 类型必须通过读取文件前若干字节(magic bytes)来检测,不能依赖 fileheader.Header.Get("Content-Type") 或后缀名。
实操建议:
- 用
io.LimitReader(file, 512)读取前 512 字节到内存缓冲区,再交由net/http.DetectContentType或更严格的库(如gabriel-vasile/mimetype)识别 - 不要仅靠
strings.HasSuffix(header.Filename, ".pdf")判断——攻击者可上传shell.php.pdf绕过 - 若检测出
application/x-executable、application/zip(非明确允许的压缩包场景)、text/html(非富文本编辑器导出场景),应直接拒绝
Beego Controller 中安全校验的典型写法
在 Upload 方法开头插入 MIME 校验逻辑,且必须在 file.Read() 或任何写入操作之前执行。Beego 不会自动帮你做这层防护。
示例关键片段:
buf := make([]byte, 512)
_, err := io.ReadFull(file, buf)
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
return ""
}
// 重置文件指针,否则后续读取会跳过前512字节
file.Seek(0, 0)
contentType := http.DetectContentType(buf)
allowedTypes := map[string]bool{
"image/jpeg": true,
"image/png": true,
"text/csv": true,
"application/pdf": true,
}
if !allowedTypes[contentType] {
c.Ctx.Output.SetStatus(400)
c.Data["json"] = map[string]string{"error": "unsupported content type"}
c.ServeJSON()
return
}
为什么 Beego 的 web.BConfig.MaxMemory 不足以防御恶意上传
MaxMemory 只控制解析 multipart 表单时内存中缓存的上限,它不阻止大文件流式写入磁盘或云存储,也不校验内容。攻击者仍可上传 500MB 的 application/octet-stream 文件,只要头部声明为 text/plain,就可能绕过基于后缀的过滤。
真正需要的防护组合是:
- 前置:Nginx 设置
client_max_body_size和client_header_timeout - Beego 层:手动 magic bytes 检测 + 白名单
contentType - 存储层:七牛 SDK 上传前再次校验(如用
storage.PutExtra传自定义元数据"x-mime",服务端回调验证)
常见误判场景与绕过点
某些合法文件会被 http.DetectContentType 误判:比如 UTF-8 BOM 开头的 CSV 可能返回 text/plain;无 BOM 的 Excel(.xlsx)常被识别为 application/zip。这不是 bug,而是标准行为。
应对方式:
- 对业务强相关的格式(如 Excel),改用专用库检测:
github.com/tealeg/xlsx尝试 OpenReader,失败则拒收 - 允许一组“模糊匹配”类型,例如把
application/zip和application/vnd.openxmlformats-officedocument.spreadsheetml.sheet都纳入 Excel 白名单 - 永远不要在检测后修改
fileheader.Header并期望 Beego 后续逻辑读取它——Beego 不消费这个字段
最易被忽略的是:校验完 MIME 后忘记调用 file.Seek(0, 0)。一旦读过一次,后续上传到七牛或保存到本地就会读空,导致上传成功但内容为空文件。


















