ParseMultipartForm内存阈值必须在读取请求体前设置,否则大文件会全量加载进内存导致OOM kill;r.ParseMultipartForm(32 << 20)推荐设为32MB。

ParseMultipartForm 内存阈值必须在读取请求体前设置
不设或晚设 r.ParseMultipartForm 的 maxMemory 参数,会导致大文件直接全量加载进内存,服务被系统 OOM kill——不是报错,是进程突然消失。
r.ParseMultipartForm(32 中的 32MB 仅控制「表单字段值」内存占用,对上传文件本体无效;超限部分写入临时磁盘,但不阻止大文件到达- 必须在任何访问
r.FormFile、r.PostForm或r.MultipartForm之前调用,否则 panic 报错"http: multipart handled by ParseMultipartForm" - 搭配
defer r.MultipartForm.RemoveAll()清理临时文件,否则磁盘空间持续增长
MaxBytesReader 必须包装 r.Body 才能真正限制总上传体积
http.MaxBytesReader 是唯一能提前截断恶意请求的手段,它作用于整个请求体(含所有字段+文件),一旦超限立即关闭连接。
- 顺序不能错:
r.Body = http.MaxBytesReader(w, r.Body, 10 必须放在 <code>r.ParseMultipartForm之前,否则无效 - 该限制是硬性上限,比如设 10MB,客户端传 11MB 就直接失败,不会进入后续解析逻辑
- 若表单含多个文件或大量文本字段,需按业务预期总负载设值,而非只看单个文件大小
文件类型校验不能依赖 filename 或 Content-Type
客户端可任意伪造 Content-Type 和扩展名,filepath.Ext(header.Filename) 和 header.Header.Get("Content-Type") 都不可信。
- 真实类型必须靠 magic bytes 判断:用
github.com/h2non/filetype读取前 262 字节,比标准库http.DetectContentType更准、支持格式更多 - 白名单只放可信 MIME 类型:
"image/png"、"application/pdf",明确拒绝"text/html"、"application/x-executable" - 若需保留扩展名,从
filetype检测结果映射(如filetype.JPEG→".jpg"),而非用原始header.Filename
文件名与路径拼接必须彻底隔离原始输入
用户传 "../../../etc/passwd" 或 "shell.php\x00.jpg" 会直接导致路径穿越或截断,os.Create(uploadDir + "/" + filename) 等同于开放任意写入。
立即学习“go语言免费学习笔记(深入)”;
- 完全丢弃
header.Filename,生成服务端唯一 ID:uuid.New().String()+ 白名单扩展名 - 目标路径必须用
filepath.Join(uploadDir, safeName)拼接,再调用filepath.Clean()并检查是否仍在预期目录内:strings.HasPrefix(absPath, absUploadDir) - 上传目录绝不能放在 Web 根目录下(如
/var/www/static/uploads/),应设为独立路径(如/var/uploads/),权限设为0750,Web server 用户无读写权
MaxBytesReader 的位置和 filepath.Clean() 后的路径归属检查——这两个动作漏掉任何一个,前面所有防护都形同虚设。


















