In this article
Entenda a injeção de template no lado do servidor em Golang
Uma vulnerabilidade de segurança não tão comum entre desenvolvedores é a injeção de template no lado do servidor (SSTI). Ainda assim, ela pode abrir caminho para riscos de segurança cada vez maiores, como inclusão de arquivos, Cross-Site Scripting (XSS) e até ataques de injeção de código.
Quando desenvolvedores usam Golang para criar aplicações web full-stack, cuidando tanto do back-end quanto do front-end, ficam mais sujeitos a cometer erros e escrever código inseguro que pode levar à SSTI.
Neste artigo, você vai conhecer uma aplicação web simples em Golang com várias vulnerabilidades de segurança e padrões de código inseguros que espero que você identifique. Depois, vamos nos aprofundar no caso específico de SSTI para entender como atacantes podem explorar essa vulnerabilidade e como a biblioteca integrada do Golang html/template é usada nesse contexto.
Como criar uma aplicação web em Golang
Vamos começar montando, etapa por etapa, os componentes básicos de uma pequena aplicação web em Golang. Usaremos os seguintes componentes:
O framework web Gin para Golang usa a biblioteca open source Go
github.com/gin-gonic/gin.O banco de dados SQLite 3 usa a biblioteca open source Go
database/sqle o driver de banco de dados SQLite "github.com/mattn/go-sqlite3".A biblioteca integrada do Go
html/templatepara compor respostas HTML usando um template declarado.
Declarando as dependências do projeto em Golang
Para começar, nossa aplicação web, representada por app.go, declara as dependências importadas para o projeto:
package main
import (
"database/sql"
"fmt"
"html/template"
"log"
"net/http"
"github.com/gin-gonic/gin"
_ "github.com/mattn/go-sqlite3"
)Se você não conhece a sintaxe de Golang, esses são nomes ou URLs usados para importar dependências. Especificamente, o driver SQLite3 usa a sintaxe de sublinhado _ para indicar que o pacote importado não é usado explicitamente no código, mas é necessário para que o código indireto que depende dele esteja presente na aplicação.
Roteador web e manipuladores de função em Golang
Agora, vamos ter uma visão geral de como organizaremos o código dessa aplicação web simples. Estas são as funções usadas, vistas de cima:
package main
import (...)
func search(c *gin.Context) {...}
func main() {
r := gin.Default()
r.GET("/search", search)
r.Run(":8080")
}No trecho de código acima, declaramos a função de entrada main() do programa em Golang. Ela define um novo roteador usando o framework web Gin, seguido de um endpoint HTTP GET em /search, que chama a função de busca que definimos. Por fim, a aplicação web é executada para processar solicitações na porta 8080.
Exibindo os resultados do banco de dados
Nesta etapa, extraímos a query string da URL para analisar a busca do usuário, usamos esse dado para consultar os registros no banco de dados, extraímos os resultados, formatamos tudo em um template HTML e enviamos a resposta.
Observação:
O trecho de código a seguir contém várias vulnerabilidades de segurança e serve apenas para fins educacionais. Não copie e cole nem use esse código fora de um exercício de aprendizado.
O código completo do nosso manipulador da função search é o seguinte:
func search(c *gin.Context) {
query := c.Query("q")
db, err := sql.Open("sqlite3", "posts.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
rows, err := db.Query(fmt.Sprintf("SELECT * FROM posts WHERE title LIKE '%%%s%%'", query))
if err != nil {
c.String(http.StatusInternalServerError, "Error executing query")
return
}
defer rows.Close()
tmpl, err := template.New("search").Parse(fmt.Sprintf(`
<h2>Results for query "%s":</h2>
<ul>
{{range .}}
<li>{{.}}</li>
{{end}}
</ul>
`, query))
if err != nil {
c.String(http.StatusInternalServerError, "Error creating template")
return
}
var results []struct {
ID int
Title string
Content string
}
for rows.Next() {
var id int
var title string
var content string
err := rows.Scan(&id, &title, &content)
if err != nil {
log.Fatal(err)
}
results = append(results, struct {
ID int
Title string
Content string
}{id, title, content})
}
err = tmpl.Execute(c.Writer, results)
if err != nil {
c.String(http.StatusInternalServerError, "Error executing template")
return
}
}Vulnerabilidades de segurança em uma aplicação web em Golang
Na implementação do código de busca no banco de dados acima, há código inseguro que não deveria estar em uma aplicação de produção.
Quais vulnerabilidades de segurança você identificou?
Se você tivesse a extensão da Snyk para IDE instalada (ela é gratuita!), identificaria essas vulnerabilidades de segurança incômodas em questão de segundos, logo após usar o atalho para salvar o arquivo:

A injeção de SQL no manipulador de busca
Ao observar rapidamente as linhas de código relevantes na função func search() que manipula a busca, vemos que a variável query recebe dados do usuário por meio da query string na URL:
query := c.Query("q")Em seguida, esse dado é passado à consulta do banco de dados sem sanitização e de forma insegura, sem usar vinculação de parâmetros:
func search(c *gin.Context) {
query := c.Query("q")
db, err := sql.Open("sqlite3", "posts.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
rows, err := db.Query(fmt.Sprintf("SELECT * FROM posts WHERE title LIKE '%%%s%%'", query))
if err != nil {
c.String(http.StatusInternalServerError, "Error executing query")
return
}
defer rows.Close()
// ... rest of the program code ...
}Esse código vulnerável permite que atacantes realizem ataques de injeção de SQL.
A SSTI no manipulador de busca
Dando continuidade ao ponto em que paramos na função func search() acima, a próxima parte do código cria um template HTML da seguinte forma:
tmpl, err := template.New("search").Parse(fmt.Sprintf(`
<h2>Results for query "%s":</h2>
<ul>
{{range .}}
<li>{{.}}</li>
{{end}}
</ul>
`, query))O uso da variável query dessa maneira, passada à biblioteca html/template pelo marcador %s, cria pelo menos dois riscos de segurança evidentes:
Cross-Site Scripting (XSS) — permite que atacantes especifiquem elementos HTML simples, que serão inseridos no template HTML sem codificação segura.
Injeção de template no lado do servidor (SSTI) — permite que atacantes forneçam sintaxe literal de template, como
{{ .Title | unsafeHTML }}, compatível comhtml/template, que será avaliada pela biblioteca.
Sobre injeção de código na biblioteca de templates do Golang
A biblioteca integrada do Golang html/template tem medidas de segurança incorporadas ao pacote que restringem o acesso a funções potencialmente perigosas do ambiente do usuário, como os.system() e outras.
No entanto, o pacote permite definir funções personalizadas que recebem dados do usuário e retornam um resultado. Assim, desenvolvedores podem definir sua própria lógica e usar APIs sensíveis e funções do ambiente do usuário da biblioteca padrão do Golang ou de outros pacotes de terceiros do ecossistema Golang.
Considere a seguinte função personalizada hipotética de template:
func dangerousFunction(input string) string {
output, err := exec.Command("bash", "-c", input).Output()
if err != nil {
return "Error executing command"
}
return string(output)
}
tmpl, err := template.New("search").Funcs(template.FuncMap{"dangerous": dangerousFunction}).Parse( /* ... */ )Observação:
A função personalizada acima é perigosa e não deve ser usada fora de um exercício de aprendizado.
Voltando ao nosso exemplo: os dados inseridos pelo usuário na query string q da URL passam para a variável query e, em seguida, para o template. Agora, eles podem ser interpretados como sintaxe especial válida de html/template se o valor do parâmetro de consulta for: {{ dangerous "rm -rf /tmp" }}.
Reforçando a segurança de aplicações em Golang
Vamos resumir o que aprendemos e as conclusões desta análise das vulnerabilidades de segurança em Golang. Embora o Go ofereça ferramentas poderosas, como o pacote html/template, para criar aplicações web, a segurança deve estar sempre em foco.
Ao entender e mitigar vulnerabilidades potenciais como injeção de SQL, SSTI e XSS, podemos aumentar a segurança do nosso código e dar um passo em direção a aplicações mais seguras em Golang.
Além disso, usar a extensão da Snyk para IDE no VS Code, IntelliJ ou em outras ferramentas de desenvolvimento ajuda a criar aplicações com código mais seguro, detectando vulnerabilidades e ajudando a corrigi-las assim que escrevemos o código.
Dicas rápidas para desenvolver com segurança em Golang
Injeção de SQL: use sempre consultas parametrizadas para evitar vulnerabilidades de injeção de SQL.
SSTI: fique atento aos possíveis riscos de SSTI, especialmente ao usar funções personalizadas de template ou integrar bibliotecas de terceiros. Use a interpolação segura de variáveis.
Sanitização de dados: sanitize todos os dados inseridos pelo usuário antes de usá-los em templates, consultas SQL ou qualquer outra parte da aplicação. Não concatene dados do usuário diretamente nem de forma insegura.
Segurança de html/template: use com cautela os recursos de segurança da biblioteca html/template, como o tipo template.HTML, e somente quando necessário. Caso contrário, você corre o risco de introduzir vulnerabilidades de XSS.
Auditorias de segurança regulares: use a Snyk para realizar auditorias de segurança periódicas na sua aplicação e identificar e corrigir possíveis vulnerabilidades de segurança no código Golang, nos módulos Go e nas dependências de terceiros e open source.
Proteja o desenvolvimento de IA generativa com a Snyk
Crie barreiras de segurança para qualquer desenvolvimento assistido por IA.