Server side template injection - Go

Server side template injection - Go

Need

Prevention of server-side template injection attacks

Context

  • Usage of Go 1.16 for building high-performance and scalable applications
  • Usage of gin-gonic/gin for building web applications in Go
  • Usage of html/template for generating HTML templates in Go programming language

Description

Non compliant code

        package main

import (
    "github.com/gin-gonic/gin"
    "html/template"
)

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

    r.GET("/welcome", func(c *gin.Context) {
        firstname := c.DefaultQuery("firstname", "Guest")
        lastname := c.Query("lastname") 

        tmpl, err := template.New("test").Parse("Hello {{.firstname}} {{.lastname}}")
        if err != nil { panic(err) }
        err = tmpl.Execute(c.Writer, map[string]interface{}{
            "firstname": firstname,
            "lastname": lastname,
        })
        if err != nil { panic(err) }
    })
    r.Run()
}
        
        

The above code is a simple Go server using the Gin framework. It has a route "/welcome" which accepts two query parameters: "firstname" and "lastname". These parameters are then used to create a personalized greeting message using Go's template package.

The vulnerability lies in the fact that the server is using user-provided input directly in a server-side template. The template.New("test").Parse function is parsing a template string that includes the user-provided "firstname" and "lastname". If an attacker provides a specially crafted string as one of these parameters, they could potentially execute arbitrary code on the server.

For example, an attacker could provide a "firstname" parameter that looks like this: {{printf "Hello, World!"}}. When this is inserted into the template and executed, it will run the printf function with the argument "Hello, World!", effectively allowing the attacker to execute arbitrary code on the server.

This type of vulnerability is known as Server Side Template Injection (SSTI). It is a serious security issue that can lead to remote code execution, data leakage, or even server takeover if not properly mitigated.

Steps

  • Sanitize user input: Validate and sanitize any user input before using it in a template. This can be done by using input validation techniques and ensuring that only safe and expected values are used.
  • Use a secure template engine: Instead of using the default template engine, consider using a secure template engine that has built-in protection against server-side template injection vulnerabilities.
  • Disable template execution: Disable the execution of template code on the server side. This can be done by configuring the template engine to disable any potentially dangerous template features, such as executing arbitrary code.
  • Use context-aware escaping: When rendering user input in templates, make sure to use context-aware escaping to prevent any injected code from being executed. This can be done by using template functions or filters that automatically escape user input based on the context in which it is used.
  • Implement strict content security policies: Implement strict content security policies to prevent the loading and execution of external scripts or resources that could be used to exploit server-side template injection vulnerabilities.
  • Regularly update dependencies: Keep all dependencies, including the framework and template engine, up to date to ensure that any security patches or fixes are applied.
  • Perform security testing: Regularly perform security testing, including vulnerability scanning and penetration testing, to identify and address any potential vulnerabilities in the application.

Compliant code

        package main

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

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

    r.GET("/welcome", func(c *gin.Context) {
        firstname := sanitize(c.DefaultQuery("firstname", "Guest"))
        lastname := sanitize(c.Query("lastname")) 

        tmpl, err := template.New("test").Parse("Hello {{.firstname}} {{.lastname}}")
        if err != nil { panic(err) }
        err = tmpl.Execute(c.Writer, map[string]interface{}{
            "firstname": firstname,
            "lastname": lastname,
        })
        if err != nil { panic(err) }
    })
    r.Run()
}

func sanitize(input string) string {
    return url.QueryEscape(input)
}
        
        

The updated code includes a new function sanitize(input string) string which uses url.QueryEscape(input) to sanitize the user input. This function is used to sanitize the firstname and lastname parameters before they are used in the template.

url.QueryEscape(input) is a function provided by Go's net/url package. It escapes the string so it can be safely placed inside a URL query.

The sanitize function is called for each user input ( firstname and lastname) before they are used in the template. This ensures that any special characters in the user input are properly escaped, preventing server-side template injection.

This is a simple and effective way to prevent server-side template injection vulnerabilities. However, it's important to note that this is just one layer of defense and should be used in conjunction with other security measures, such as using a secure template engine, disabling dangerous template features, implementing strict content security policies, keeping dependencies up to date, and regularly performing security testing.

References