Symmetric denial of service - Go

Symmetric denial of service - Go

Need

Prevention of repeated requests causing server unresponsiveness

Context

  • Usage of Go 1.16 for building high-performance and scalable applications
  • Usage of Gin framework for building web applications in Go

Description

Non compliant code

        package main

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

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

	r.GET("/resource-intensive-endpoint", func(c *gin.Context) {
		time.Sleep(10 * time.Second) // Simulating a resource-intensive task
		c.JSON(200, gin.H{
			"message": "request processed",
		})
	})

	r.Run()
}
        
        

The code above is a simple Go application that uses the Gin web framework. It has a single GET endpoint /resource-intensive-endpoint which simulates a resource-intensive task by forcing the server to sleep for 10 seconds before responding.

This endpoint is vulnerable to a Symmetric Denial of Service (DoS) attack. An attacker could repeatedly send requests to this endpoint and cause the server to become unresponsive. This is because the server is forced to wait for 10 seconds for each request, regardless of how many requests it receives. If the server receives a large number of requests in a short period of time, it will be unable to process them all and will become unresponsive.

This vulnerability could be exploited to cause a denial of service, making the server unavailable to legitimate users. It is a serious issue that needs to be addressed to ensure the availability of the server.

Steps

  • Implement rate limiting to restrict the number of requests that can be made to the resource-intensive endpoint within a certain time frame.
  • Use timeouts to limit the maximum amount of time a request can take to be processed.
  • Optimize the resource-intensive task to reduce its execution time or resource consumption.
  • Consider offloading resource-intensive tasks to background workers or separate services to avoid blocking the main server.
  • Monitor server performance and resource usage to identify and address any potential bottlenecks or performance issues.

Compliant code

        package main

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

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

	// Create a limiter struct.
	limiter := tollbooth.NewLimiter(1, nil)

	r.GET("/resource-intensive-endpoint", tollbooth_gin.LimitHandler(limiter), func(c *gin.Context) {
		ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
		defer cancel()

		select {
		case <-time.After(10 * time.Second): // Simulating a resource-intensive task
			c.JSON(200, gin.H{
				"message": "request processed",
			})
		case <-ctx.Done():
			c.JSON(408, gin.H{
				"message": "request took too long",
			})
		}
	})

	r.Run()
}
        
        

The above code implements a rate limiter and a timeout for the resource-intensive endpoint to prevent a symmetric denial of service attack.

1. Rate Limiter: The tollbooth.NewLimiter(1, nil) function creates a new rate limiter that allows 1 request per second. The tollbooth_gin.LimitHandler(limiter) middleware is then used to apply this rate limiter to the resource-intensive endpoint.

2. Timeout: The context.WithTimeout(c.Request.Context(), 5*time.Second) function creates a new context that will automatically be cancelled after 5 seconds. This context is passed to the resource-intensive task, which will be cancelled if it takes longer than 5 seconds to complete.

If the resource-intensive task completes within the timeout, a JSON response with the message "request processed" is returned. If the task does not complete within the timeout, a JSON response with the message "request took too long" is returned.

This solution effectively mitigates the risk of a symmetric denial of service attack by limiting the rate at which requests can be made to the resource-intensive endpoint and by ensuring that these requests do not take too long to process.

References