c.formfile("file")拿不到真实文件类型是因为它返回的*multipart.fileheader中header.get("content-type")是客户端伪造的,需用filetype库基于magic bytes嗅探前512字节来校验真实mime。

为什么 c.FormFile("file") 拿不到文件类型?
因为 c.FormFile() 只返回 *multipart.FileHeader,它自带的 Header.Get("Content-Type") 是客户端“声称”的 MIME 类型(可伪造),不是真实内容类型。比如用户把 evil.exe 改名成 report.pdf 再设 Content-Type: application/pdf,FileHeader.Header 就会照单全收。
用 mime_content_type() 还是 finfo_open()?
Go 里没有 PHP 那种 mime_content_type(),得靠 finfo 对应的 net/http/sniff 或更可靠的 golang.org/x/net/html/charset 不适用——上传校验必须用二进制嗅探。推荐直接上 github.com/h2non/filetype 库(轻量、覆盖全、无 CGO):
file, err := c.FormFile("avatar")
if err != nil {
return c.String(http.StatusBadRequest, "no file")
}
src, err := file.Open()
if err != nil {
return c.String(http.StatusInternalServerError, "open failed")
}
defer src.Close()
// 读前 512 字节做 sniff
buf := make([]byte, 512)
_, _ = src.Read(buf) // 忽略 read error,filetype 会处理
kind, _ := filetype.Match(buf, "")
if kind == filetype.Unknown || (kind.Extension != "jpg" && kind.Extension != "png") {
return c.String(http.StatusBadRequest, "unsupported image type")
}
-
filetype.Match()基于 magic bytes 判断,比扩展名和 header 都可靠 - 务必限制读取长度(512 字节足够),避免大文件阻塞
- 不要用
io.ReadAll(src)全读——内存爆炸风险
如何同时校验扩展名、size 和真实 MIME?
生产环境必须三者叠加:扩展名做第一道过滤(快),size 控制资源消耗(防 DoS),真实 MIME 做最终裁决(防伪造)。顺序不能错:
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
file, _ := c.FormFile("doc")
if file.Size > 10*1024*1024 { // 先看 size
return c.String(http.StatusBadRequest, "too large")
}
if !strings.HasSuffix(strings.ToLower(file.Filename), ".pdf") { // 再看扩展名
return c.String(http.StatusBadRequest, "ext not allowed")
}
// 最后 sniff 真实类型
src, _ := file.Open(); defer src.Close()
buf := make([]byte, 512); src.Read(buf)
kind, _ := filetype.Match(buf, "")
if kind.MIME.Value != "application/pdf" {
return c.String(http.StatusBadRequest, "MIME mismatch")
}
- 扩展名检查用
strings.HasSuffix(),别用正则或path.Ext()(后者对a.b.pdf返回.pdf,但a.pdf.bak也过得了) -
file.Size是可信的——内核在写临时文件时就统计好了,不会被伪造 - 如果用
c.Request().ParseMultipartForm(0)禁用自动解析,file.Size仍可用,但file.Open()会失败,得自己从multipart.Part流式读
视频文件怎么区分 MP4 和 MOV?
两者都是 ISO Base Media 文件,magic bytes 高度相似。filetype 默认会把 MOV 识别为 video/quicktime,MP4 为 video/mp4,但某些编码的 MOV 也会被标成 video/mp4。真要严格区分,得解析 moov box:
- 简单场景:接受
video/mp4和video/quicktime都算合法视频 - 强校验场景:用
github.com/edgeware/mp4ff解析ftypbox,检查major_brand是mp42还是qt - 别依赖
file.Header.Get("Content-Type")——浏览器上传视频时经常填video/mp4即使是 MOV
真实 MIME 嗅探永远只能到“容器层”,编解码细节(H.264 vs AV1)得靠 FFmpeg,那已超出上传校验范畴。










