Inappropriate coding practices - Eval function - Go

Inappropriate coding practices - Eval function - Go

Need

Secure input validation and sanitization

Context

  • Usage of Go 1.16 for building efficient and scalable applications
  • Usage of gin-gonic/gin for building web applications in Go
  • Usage of anko/vm for embedding a scripting language in Go applications

Description

Non compliant code

        package main

import (
	"github.com/gin-gonic/gin"
	"github.com/mattn/anko/vm"
)

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

	r.GET("/eval", func(c *gin.Context) {
		script := c.Query("script")
		res, err := vm.Execute(script)
		if err != nil {
			c.JSON(500, gin.H{
				"message": err.Error(),
			})
			return
		}
		c.JSON(200, gin.H{
			"result": res,
		})
	})

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

This Go application uses the Gin web framework and the Anko library to execute user-provided scripts. The application listens on port 8080 and has a single endpoint, /eval, which accepts GET requests.

The endpoint handler function retrieves the script query parameter from the incoming request using c.Query("script"). This script string is then passed to vm.Execute(script), which evaluates the script and returns the result.

The main vulnerability lies in the fact that the user-provided script is passed directly to vm.Execute(script) without any validation or sanitization. This means that a user can provide any script they want, which will be executed by the server. This is an example of an "eval injection" vulnerability, where user input is directly evaluated or executed.

An attacker could exploit this vulnerability by providing a malicious script as the script query parameter. This could allow them to execute arbitrary commands on the server, potentially leading to data leakage, data corruption, denial of service, or even full system compromise.

To fix this vulnerability, you should never directly execute user-provided scripts or code. If you need to allow users to provide some form of script or code, it should be in a restricted, sandboxed environment, and you should thoroughly validate and sanitize the user input to ensure it does not contain any malicious commands or code.

Steps

  • Avoid using the eval function altogether.
  • Validate and sanitize user input before executing any code.
  • Use a whitelist approach to only allow specific safe operations.
  • Implement input validation and filtering mechanisms to prevent code injection attacks.
  • Use parameterized queries or prepared statements when interacting with databases.
  • Implement proper error handling to prevent sensitive information leakage.
  • Regularly update and patch the dependencies used in the application.
  • Educate developers about secure coding practices and the risks associated with eval function.

Compliant code

        package main

import (
	"github.com/gin-gonic/gin"
	"net/url"
	"strings"
)

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

	r.GET("/eval", func(c *gin.Context) {
		script := c.Query("script")

		// Validate and sanitize user input
		safeScript := sanitizeInput(script)

		// If the script is not safe, return an error
		if safeScript == "" {
			c.JSON(400, gin.H{
				"message": "Invalid script",
			})
			return
		}

		// Execute the safe script
		// res, err := vm.Execute(safeScript) // Uncomment this line if you want to execute the script
		// For the purpose of this example, we will just return the sanitized script
		c.JSON(200, gin.H{
			"result": safeScript,
		})
	})

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

// sanitizeInput function is used to sanitize and validate the user input
func sanitizeInput(input string) string {
	// For the purpose of this example, we will just check if the input contains "bad" string
	// In real-world applications, you should use more sophisticated sanitization and validation techniques
	if strings.Contains(input, "bad") {
		return ""
	}

	// URL encode the input to prevent code injection attacks
	return url.QueryEscape(input)
}
        
        

The original code was vulnerable because it used the vm.Execute function (similar to eval in other languages) to execute a script provided by the user through a query parameter without any validation or sanitization. This could allow an attacker to execute arbitrary code on the server.

The fixed code adds a sanitizeInput function that is used to sanitize and validate the user input before executing it. In this example, the function checks if the input contains the string "bad" and returns an empty string if it does. It also URL encodes the input to prevent code injection attacks. If the sanitized script is empty (which means it was not safe), the server returns a 400 error with a message "Invalid script".

Please note that this is a very basic example of input sanitization and validation. In real-world applications, you should use more sophisticated techniques and libraries to ensure the safety of your application.

Also, it's important to note that using functions like eval or vm.Execute is generally considered a bad practice because of the high risk of code injection attacks. If possible, you should avoid using these functions altogether and find safer alternatives to achieve the same functionality.

References