Server-side Template Injection
ID |
go.server_side_template_injection |
Severity |
critical |
Resource |
Injection |
Language |
Go |
Tags |
CWE:1336, NIST.SP.800-53, OWASP:2021:A3, PCI-DSS:6.5.1 |
Description
Template engines allow dynamic generation of content (HTML, JSON, email messages…) merged with data. Unsafely embedding untrusted input in templates executed enables Server-Side Template Injection (SSTI), when template rendering is done at the server-side.
Rationale
Template injection can happen through a design flaw, or if the application allows users to upload templates without proper validation. Such flaws can be found in blogs, wikis, or content management tools, to name a few.
If an attacker is able to send parts of the template, or select which template to use, the rendered content could be influenced by the attacker, and the attack payload could include code execution or sensitive information leakage.
Some template engines allow a URL or path to select the template to use. If the attacker can choose URL/path to use, we are one step below remote code execution, command injection, or web defacement, depending on how the attack is executed
An application vulnerable to template injection can open the door to arbitrary code execution, if combined with local file injection techniques.
The detector will report a vulnerability if user-controlled input is concatenated in template source or the reference to a template resource without neutralization.
package main
import (
"html/template"
"net/http"
)
func main() {
http.HandleFunc("/", index)
http.ListenAndServe(":8080", nil)
}
func index(w http.ResponseWriter, r *http.Request) {
// Unsafe: directly using user input in templates
userInput := r.URL.Query().Get("user_input")
tmpl := `Result: ` + userInput
t, err := template.New("webpage").Parse(tmpl) // FLAW
if err != nil {
http.Error(w, "Error parsing template", http.StatusInternalServerError)
return
}
t.Execute(w, nil)
}
Remediation
If possible, never let untrusted input be concatenated with other text in a template, or used in the location (path or URL) of the template resource that the template engine uses. Allowing users to upload or edit a template is also dangerous. Passing user input into templates as "context parameters" is normally a safe alternative. If the content is rendered in a browser, and the evaluated expressions from user input are not properly escaped, cross-site scripting may be possible.
If users can choose the template, use a whitelist. This means the user can choose which template to apply from a fixed list of allowed template resources.
The impact of SSTI attacks could be reduced by using a logic-less engine like Mustache or Handlebars. If this is not possible, review the template engine documentation for security advice.
Note: Some template engines offer a 'sandboxed' mode for this purpose, but such sandboxes typically fail against a determined attacker who finds ways to escape the sandbox. Do not trust such sandboxing capabilities.
See the following remediated Golang code:
package main
import (
"html/template"
"net/http"
)
func main() {
http.HandleFunc("/", index)
http.ListenAndServe(":8080", nil)
}
func index(w http.ResponseWriter, r *http.Request) {
userInput := r.URL.Query().Get("user_input")
tmpl := `{{define "webpage"}}Result: {{.}}{{end}}`
t, err := template.New("webpage").Parse(tmpl)
if err != nil {
http.Error(w, "Error parsing template", http.StatusInternalServerError)
return
}
t.ExecuteTemplate(w, "webpage", userInput)
}
In this example, {{.}}
is used in the template to safely escape user input.
Configuration
The detector has the following configurable parameters:
-
sources
, that indicates the source kinds to check. -
neutralizations
, that indicates the neutralization kinds to check.
Unless you need to change the default behavior, you typically do not need to configure this detector.
References
-
CWE-1336 : Improper Neutralization of Special Elements Used in a Template Engine.
-
OWASP - Top 10 2021 Category A03 : Injection.