必须完成全部校验:真实 mime 类型(用 detectcontenttype 读前 512 字节)、扩展名一致性(由 mime 反查白名单映射,禁双后缀)、大小限制(maxbytesreader+流式)、路径安全(过滤 windows 设备名)、内容重编码。

必须在保存前完成全部校验:真实 MIME 类型、扩展名一致性、大小限制、路径安全、内容重编码——缺一不可,任何一步跳过都可能被绕过。
用 DetectContentType 校验真实 MIME 类型,不信任 Content-Type 头
客户端可随意伪造 Content-Type(比如把 PHP 文件标为 image/jpeg),仅靠它做白名单等于形同虚设。正确做法是读取文件前 512 字节,交给 net/http.DetectContentType 判定真实类型。
- 调用
fileHeader.Open()获取io.ReadCloser,再用io.LimitReader(file, 512)安全读取 - 空文件或小于 512 字节时,
DetectContentType可能返回text/plain,需额外检查fileHeader.Size == 0 - 白名单只放明确需要的类型,例如:
"image/jpeg"、"image/png"、"application/pdf",禁用"text/html"、"application/x-executable" - 注意:该函数不识别 WebP 或 AVIF 等较新格式,如需支持,改用
github.com/h2non/filetype
扩展名必须与 MIME 类型逻辑一致,且仅从白名单映射生成
用户传来的 fileHeader.Filename 含 .php.jpg 这类双后缀是常见绕过手法。不能用 filepath.Ext() 提取扩展名再比对,而应由 MIME 类型反查允许的扩展名。
- 构建映射表:
mimeToExt = map[string]string{"image/jpeg": ".jpg", "image/png": ".png", "application/pdf": ".pdf"} - 若
DetectContentType返回"image/jpeg",但用户原始名是shell.php.jpg,仍只取".jpg",绝不保留.php - 若 MIME 类型不在映射表中(如返回
application/octet-stream),直接拒绝,不 fallback 到扩展名 - Windows 下还要过滤设备名:
CON、AUX、NUL等不能作文件名主体
用 http.MaxBytesReader 和流式读取双重限制大小
r.ParseMultipartForm(32 只控内存缓冲,不限制实际上传体积;攻击者可构造超大文件写入磁盘填满空间。必须组合两层限制。
- 第一层:在解析请求体前,用
r.Body = http.MaxBytesReader(w, r.Body, 10(如 10MB)截断整个请求体 - 第二层:调用
r.MultipartReader()手动遍历每个multipart.Part,对每个文件用io.LimitReader(part, maxFileSize)单独限流 - 同时累加已读总字节数,超过总上限(如 100MB)立即
http.Error(w, "", http.StatusRequestEntityTooLarge)并关闭连接 - 避免使用
r.FormFile,它内部会触发完整解析,失去流控能力
安全重命名 + 路径逃逸防护 + 原子写入
原始 fileHeader.Filename 是完全不可信输入,含 ../、\x00、控制字符等。拼接路径即等于开放任意写权限。
- 彻底丢弃原始名,用
crypto/rand.Read生成 16 字节随机 ID,转hex.EncodeToString或base32.StdEncoding.EncodeToString - 拼接路径前必须
filepath.Clean(),再用strings.HasPrefix(absPath, absUploadDir)确认未逃逸出目标目录 - 写入先到临时路径(如
uploadDir + "/.tmp-" + uuid),写完os.Rename原子替换,防止中断留下脏文件 - 上传目录权限设为
0750,属主为 Go 进程用户,Web server 用户(如www-data)无读写权
最易被忽略的是「重编码」环节:图片文件即使 MIME 和扩展名都合法,也可能嵌入恶意 payload(如 GIF 中藏 PHP 代码),某些渲染库(ImageMagick)会二次执行。生产环境必须对图片做解码+再编码,剥离 EXIF、注释、隐藏帧——这步无法跳过,也不能靠前端压缩代替。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











