Improper control of interaction frequency - Go

Improper control of interaction frequency - Go

Need

Enforcement of rate limiting to control user interaction frequency

Context

  • Usage of Go 1.16 for developing high-performance and efficient applications
  • Usage of gin-gonic/gin for building web applications in Go

Description

Non compliant code

        package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    
    r.POST("/post", func(c *gin.Context) {
        // process post
    })
    
    r.Run() // listen and serve on 0.0.0.0:8080
}
        
        

The above code represents a simple backend server using the Gin framework in Go. It has a POST endpoint /post where users can send their requests.

The vulnerability in this code is that there is no control over the frequency of interactions. That is, a user can send as many requests as they want in a short period of time. This can lead to a Denial of Service (DoS) attack where the server gets overwhelmed with too many requests, causing it to slow down or even crash, thus denying service to legitimate users.

This lack of rate limiting can also allow an attacker to brute force their way into the system. For example, if there is a login functionality, an attacker can try as many username/password combinations as they want until they find the right one.

In the context of this code, the vulnerability is in the /post endpoint where the function handling the requests does not implement any form of rate limiting.

Steps

  • Implement rate limiting to restrict the number of requests a user can make within a specific time frame.
  • Use a middleware to intercept incoming requests and check if the user has exceeded the allowed limit.
  • Track the number of requests made by each user and the timestamp of the last request.
  • If the user has exceeded the limit, return an appropriate error response, such as HTTP 429 Too Many Requests.
  • Choose an appropriate rate limit strategy, such as a fixed number of requests per minute or a sliding window algorithm.
  • Consider using a distributed rate limiting solution if your application is deployed across multiple servers.
  • Test the rate limiting implementation to ensure it is working as expected and effectively mitigating the vulnerability.

Compliant code

        package main

import (
	"github.com/gin-gonic/gin"
	"github.com/didip/tollbooth"
	"github.com/didip/tollbooth_gin"
	"time"
)

func main() {
	r := gin.Default()

	// Create a limiter struct.
	limiter := tollbooth.NewLimiter(1, nil) // limit to 1 request per second
	limiter.SetIPLookups([]string{"RemoteAddr", "X-Forwarded-For", "X-Real-IP"})
	limiter.SetMessage("You have reached maximum request limit.")
	limiter.SetMessageContentType("text/plain; charset=utf-8")
	limiter.SetTokenBucketExpirationTTL(time.Hour)

	r.POST("/post", tollbooth_gin.LimitHandler(limiter), func(c *gin.Context) {
		// process post
	})

	r.Run() // listen and serve on 0.0.0.0:8080
}
        
        

The above code implements rate limiting in a Go backend application using the Gin framework and the Tollbooth library.

The tollbooth.NewLimiter(1, nil) function creates a new limiter struct that limits the number of requests to 1 per second. The SetIPLookups method is used to specify the headers that should be used to identify the client's IP address.

The SetMessage and SetMessageContentType methods are used to set the error message and content type that will be returned if a client exceeds the rate limit.

The SetTokenBucketExpirationTTL method is used to set the expiration time for the token bucket, which is used to track the number of requests made by each client.

The tollbooth_gin.LimitHandler(limiter) function is used as a middleware in the route handler for the POST endpoint. This middleware intercepts incoming requests and checks if the client has exceeded the rate limit. If the limit has been exceeded, it returns an HTTP 429 Too Many Requests response with the error message specified earlier.

This implementation effectively mitigates the vulnerability by preventing a client from making too many requests in a short period of time, which could potentially lead to a denial of service (DoS) attack.

References