Insecure exceptions - Empty or no catch - Go

Insecure exceptions - Empty or no catch - Go

Need

Proper exception handling and error management

Context

  • Usage of Go 1.16 for building high-performance and scalable 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.GET("/ping", func(c *gin.Context) {
		defer func() {
			if r := recover(); r != nil {
				// Empty catch
			}
		}()

		// Some code that might throw an exception
		panic("Something went wrong")
	})

	r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
        
        

In the above code, we have a GET route /ping that is supposed to handle incoming requests. Inside this route, we have a defer statement with a recover function that is supposed to catch any panic that might occur in the code that follows.

However, the catch block is empty. This means that if the panic("Something went wrong") statement is executed, the program will indeed catch the panic, but it won't do anything about it. As a result, the application will continue running as if nothing happened, even though a serious error might have occurred.

This is a serious vulnerability because it can make the application temporarily or permanently unavailable by presenting errors that are not being handled. Moreover, it makes it harder to debug the program, as the exceptions are being swallowed silently.

The correct way to handle exceptions in this case would be to at least log the error and return a proper HTTP response.

Steps

  • Add a proper catch block to handle the exception.
  • Inside the catch block, handle the error gracefully by logging or returning an appropriate response.
  • Avoid using empty catch blocks as they hide potential issues and make debugging difficult.
  • Consider using a centralized error handling mechanism to handle exceptions consistently across the application.

Compliant code

        package main

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

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

	r.GET("/ping", func(c *gin.Context) {
		defer func() {
			if r := recover(); r != nil {
				log.Println("Recovered from panic: ", r)
				c.JSON(500, gin.H{
					"message": "An error occurred, please try again later",
				})
			}
		}()

		// Some code that might throw an exception
		panic("Something went wrong")
	})

	r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
        
        

The updated code now includes a proper catch block to handle the exception. In the event of a panic, the deferred function will recover from the panic and execute the code within the catch block.

The catch block logs the error message and returns a JSON response with a status code of 500, indicating an internal server error. This provides a more graceful way of handling the error, rather than allowing the application to crash.

This approach ensures that any potential issues are not hidden and makes debugging easier. It also provides a consistent way of handling exceptions across the application.

Remember, it's important to avoid using empty catch blocks as they can hide potential issues and make debugging difficult. Consider using a centralized error handling mechanism to handle exceptions consistently across your application.

References