Prevention of server-side template injection attacks
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.
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.