Gin默认不拦截超大请求体,因其框架本身不自动限制r.Body大小,必须显式使用http.MaxBytesReader或配置BodySizeLimit,否则恶意大请求会直接导致OOM;失效主因是限流未在任何r.Body读取前完成包装。

为什么 Gin 默认不拦超大请求体
Gin 框架本身不自动限制 r.Body 大小,gin.Default() 或 gin.New() 创建的引擎完全放行任意体积的请求体。攻击者发一个 2GB 的 POST /login,Gin 会尝试全部读进内存——不是报错,而是直接卡死或 OOM kill。这不是配置遗漏,是设计使然:框架把资源控制权交还给开发者。
BodySizeLimit 中间件为何有时失效
gin.Engine.MaxMultipartMemory 和 gin.Engine.BodySizeLimit(v1.9+)本质是调用 http.MaxBytesReader,但只在框架内部读取 r.Body 前生效。常见失效点:
- JWT 中间件提前调用
r.ParseForm()或io.ReadAll(r.Body),原始r.Body已被读空,后续限流包装无效 - handler 里绕过
c.ShouldBindJSON(),直接用json.NewDecoder(c.Request.Body).Decode(),没手动套http.MaxBytesReader - 自定义
gin.Engine实例时覆盖了默认gin.DefaultWriter,导致 v1.9+ 自动启用的限流被禁用
multipart 文件上传的真实限制逻辑
r.ParseMultipartForm(maxMemory) 的 maxMemory 参数 ≠ 总上传大小限制。它只控制「内存中缓存的表单字段和小文件」上限,超出部分自动落盘——攻击者可发一个 500MB 文件 + 1KB 文本字段,maxMemory = 8 (8MB)完全挡不住。
真正有效的组合:
- 前置总大小拦截:
r.Body = http.MaxBytesReader(c.Writer, r.Body, 50(如 50MB) - 再调
r.ParseMultipartForm(8 ,让小数据走内存、大数据落临时文件 - 用
c.FormFile("file")获取句柄操作磁盘文件,避免c.PostForm("field")尝试加载全部内容
全局限流选 MaxBytesHandler 还是 MaxBytesReader
http.MaxBytesHandler 包裹整个 http.Handler,在路由匹配前就拦截超限请求,返回 413 并关闭连接,最安全、无遗漏风险;但它无法按接口差异化——/login 和 /upload 只能共用同一上限。
http.MaxBytesReader 是 per-handler 控制,需每个 handler 开头手动赋值:r.Body = http.MaxBytesReader(...)。它灵活,但漏写或顺序错(比如写在 c.ShouldBindJSON() 后)就等于没设。
推荐策略:
- 所有接口统一宽松上限(如 10MB)→ 直接用
http.MaxBytesHandler - 需要差异化(如
/login限 2MB,/upload限 100MB)→ 在中间件里根据c.Request.URL.Path动态调用http.MaxBytesReader
无论哪种,都必须同步设置 http.Server.ReadTimeout,否则恶意客户端慢速发送(slowloris 类攻击)仍能长期占用连接。


















