我正在寻找一小部分限速中间件:
允许我为每个远程IP设置合理的速率(例如,10 req/s)
可能(但不一定)允许爆发
丢弃(关闭?)超过速率的连接并返回HTTP 429
然后,我可以围绕身份验证路由或其他可能容易遭受暴力攻击的路由(即使用过期的令牌密码重置URL等).有人粗暴地强制使用16或24字节令牌的可能性非常低,但是进行额外步骤并没有什么坏处.
我查看了https://code.google.com/p/go-wiki/wiki/RateLimiting,但我不确定如何将其与http.Request(s)进行协调.此外,我不确定在任何时间段内我们如何"跟踪"来自给定IP的请求.
理想情况下,我最终得到类似的东西,注意我是在反向代理(nginx)的后面,所以我们正在检查REMOTE_ADDR
HTTP头而不是使用r.RemoteAddr
:
// Rate-limiting middleware func rateLimit(h http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { remoteIP := r.Header.Get("REMOTE_ADDR") for req := range (what here?) { // what here? // w.WriteHeader(429) and close the request if it exceeds the limit // else pass to the next handler in the chain h.ServeHTTP(w, r) } } // Example routes r.HandleFunc("/login", use(loginForm, rateLimit, csrf) r.HandleFunc("/form", use(editHandler, rateLimit, csrf) // Middleware wrapper, for context func use(h http.HandlerFunc, middleware ...func(http.HandlerFunc) http.HandlerFunc) http.HandlerFunc { for _, m := range middleware { h = m(h) } return h }
我在这里感谢一些指导.
您链接的速率限制示例是一般的.它使用范围,因为它通过通道获取请求.
这是一个与HTTP请求不同的故事,但这里没有什么真正复杂的.请注意,您不会迭代请求或任何事件的通道 - 分别为每个传入请求调用HandlerFunc .
func rateLimit(h http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { remoteIP := r.Header.Get("REMOTE_ADDR") if exceededTheLimit(remoteIP) { w.WriteHeader(429) // it then returns, not passing the request down the chain } else { h.ServeHTTP(w, r); } } }
现在,选择存储速率限制计数器的位置取决于您.一种解决方案是简单地使用将IP映射到其请求计数器的全局映射(不要忘记安全的并发访问).但是,您必须知道请求发生了多久以前.
Sergio建议使用Redis.它的键值特性非常适合这种简单的结构,并且您可以免费使用.