Beego 中全局限流必须下沉至 http.Server 启动前,因 Controller.Run 在 goroutine 中执行已晚于连接建立;需禁用自动启动、自定义 net.Listener 在 Accept 后原子计数限流,并用 http.MaxBytesReader 限制上传总大小。

Beego 中不能直接用 rate.Limiter 做全局限流
Beego 的 Controller.Run 已在 goroutine 中执行,所有中间件和业务逻辑都晚于连接建立、TLS 握手、请求头读取 —— 此时恶意连接早已吃掉 fd、内存和调度资源。直接在 Prepare() 里 new 一个 rate.Limiter 或塞进 context.WithValue,等于给每个请求配个桶,既泄漏内存又锁竞争;用单例桶则所有 IP 共享令牌,限流失效。
必须把限流下沉到 http.Server 启动前
Beego 底层仍是 net/http,真正能拦截流量的位置只有两个:http.Server.ReadTimeout/IdleTimeout 控制连接生命周期,以及自定义 net.Listener 在 Accept() 后立刻检查连接数。Beego 不暴露 Listener 接口,所以得绕过 bee run,自己调 http.Serve():
- 禁用 Beego 自动启动:设
beego.BConfig.Listen.EnableHTTP = false - 构造自定义
net.Listener,包装原 listener,Accept()后用atomic.LoadInt64(&connCount)判断是否超限,超限则conn.Close()并记录日志 - 用
atomic.AddInt64(&connCount, 1)和defer atomic.AddInt64(&connCount, -1)管理计数,别碰sync.WaitGroup
上传接口必须用 http.MaxBytesReader 卡死总大小
Beego 的 MaxMemory(对应 r.ParseMultipartForm)只控内存缓存,不拦磁盘写入。攻击者发一个 1KB 表单 + 99MB 文件,MaxMemory=8 完全无效。正确姿势是:
- 在
func main()启动前,用http.MaxBytesHandler包裹整个beego.BeeApp.Handlers,设全局上限(如 50MB) - 对敏感路径如
/api/v1/upload,在路由匹配后、Controller.Prepare()前,手动重置r.Body = http.MaxBytesReader(w, r.Body, 100<<20) - 后续再调
r.ParseMultipartForm(8 << 20),确保小字段走内存、大文件落盘但总量可控
Beego 的 LimitRouter 是伪限流,仅防误操作
Beego 提供的 LimitRouter 中间件本质是统计请求数+简单 sleep,它运行在 Controller 生命周期内,对 Slowloris、SYN Flood、空连接完全无感。它返回的 429 是业务层响应,TCP 连接仍保持打开,IdleTimeout 未触发,攻击者可无限复用连接打满并发数。真实防护必须依赖:
-
http.Server.IdleTimeout ≤ 30s,且显式设TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))关 HTTP/2 - 响应头加
w.Header().Set("Connection", "close"),尤其对 IoT 设备或爬虫客户端 - 前置 Nginx 配
limit_conn和limit_req,按$binary_remote_addr+$request_uri双维度限流,Beego 只处理已放行的请求


















