Go HTTP服务端需用http.MaxBytesReader限制总请求体大小(如10MB),再调用ParseMultipartForm设内存缓冲上限(如32MB,且不超过前者),否则易遭OOM攻击。

Go HTTP 服务端如何设置上传文件大小限制
Go 标准库 http.Request 默认不限制请求体大小,ParseMultipartForm 调用前若不设限,攻击者可发送超大文件耗尽内存或触发 OOM。关键动作是调用 request.ParseMultipartForm 前,显式传入最大字节数。
-
request.ParseMultipartForm(maxMemory)中的maxMemory是内存缓冲上限(单位字节),不是文件总大小上限;超出部分会写入临时磁盘,但整个请求体仍可能极大 - 真正限制「整个请求体」大小,需在读取请求体前用
http.MaxBytesReader包裹request.Body - 典型组合:先用
MaxBytesReader限制总请求体(如 10MB),再调用ParseMultipartForm设定内存缓冲(如 32MB)——注意后者不能超过前者,否则无意义
func uploadHandler(w http.ResponseWriter, r *http.Request) {
const maxRequestSize = 10 << 20 // 10MB
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
if err := r.ParseMultipartForm(32 << 20); err != nil {
http.Error(w, "request too large", http.StatusBadRequest)
return
}
// 后续处理...
}
为什么只靠 ParseMultipartForm 的 maxMemory 不够安全
ParseMultipartForm 的 maxMemory 参数仅控制「内存中缓存的 multipart 数据量」,不阻止客户端发送远超该值的原始请求体。如果未配合 MaxBytesReader,攻击者可构造一个 1GB 的请求体,其中只有前 32MB 进内存,其余写磁盘,但已成功消耗服务端 I/O 和时间,且绕过内存限制。
- 错误做法:
r.ParseMultipartForm(32 单独使用 → 无法防御大请求体 - 正确顺序:必须先 wrap
r.Body,再 parse;否则MaxBytesReader失效 - 注意:一旦
r.Body被读取(例如调用ParseForm或直接io.ReadAll),再 wrap 就晚了
Go Gin 框架中限制上传大小的等效写法
Gin 内部基于标准库,但封装了更简洁的配置入口。全局限制通过 gin.SetMode(gin.ReleaseMode) 后调用 gin.DefaultWriter 不影响,重点是设置 gin.MaxMultipartMemory 并启用 MaxBytesReader 等效机制。
- Gin v1.9+ 默认启用请求体大小检查,但需手动设置
engine.MaxMultipartMemory = 8 (单位字节) - 若要限制总请求体(含 header + body),仍需中间件:用
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, limit) - 注意:Gin 的
c.Request.FormFile会隐式调用ParseMultipartForm,所以MaxMultipartMemory必须早于首次 FormFile 调用
router := gin.Default()
router.MaxMultipartMemory = 8 << 20 // 8MB 内存缓冲
// 全局请求体大小限制中间件
router.Use(func(c *gin.Context) {
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 10<<20)
c.Next()
})
额外必须做的上传安全校验点
大小限制只是基础,真实生产环境还需叠加内容校验。Go 本身不自动校验 MIME 类型或文件扩展名,这些全靠开发者手动做。
- 不要信任
Header.Get("Content-Type")或前端传的filename后缀;应读取文件头(magic bytes)用net/http.DetectContentType或第三方库如gabriel-vasile/mimetype校验实际类型 - 临时文件路径由
os.TempDir()决定,确保该目录不可执行、不可遍历;上传后立即重命名并移出临时区 - 若允许用户下载上传文件,务必设置
Content-Disposition: attachment并禁用X-Content-Type-Options: nosniff外的其他执行风险头 - 避免将用户输入的
filename直接拼进os.OpenFile路径,防止../路径遍历
MaxBytesReader 的包裹时机和位置——它必须是第一个读取 Body 的操作,且不能晚于任何解析类方法调用。









