In this article
Como escrever código seguro em Go
Vamos falar sobre a importância de escrever código seguro em Go. Escrever código seguro é uma responsabilidade fundamental dos desenvolvedores, especialmente em uma linguagem como Go, amplamente usada na criação de sistemas escaláveis e eficientes. A simplicidade e o desempenho de Go fazem dela uma escolha popular para aplicações nativas da nuvem, principalmente por sua integração com o ecossistema de ferramentas do Kubernetes. No entanto, vulnerabilidades de segurança podem comprometer esses benefícios. Garantir que seu código Go seja seguro protege sua aplicação, resguarda os dados dos usuários e mantém a confiança.
Quais dessas vulnerabilidades de segurança comuns afetam aplicações escritas em Go?
Visão geral das vulnerabilidades de segurança comuns em aplicações Go
Aplicações Go estão sujeitas a várias vulnerabilidades de segurança que podem comprometer a integridade, a confidencialidade e a disponibilidade da sua aplicação (também conhecidas como tríade CIA no jargão de segurança). Algumas das vulnerabilidades mais comuns incluem:
Injeção de SQL ocorre quando entradas não confiáveis são concatenadas em consultas SQL, permitindo que invasores manipulem sua execução.
Injeção de comandos ocorre quando entradas de usuário são tratadas incorretamente em comandos de shell, levando à execução de comandos arbitrários.
Falsificação de solicitação do lado do servidor (SSRF) ocorre quando uma aplicação acessa uma URL fornecida pelo usuário, o que pode permitir que invasores façam solicitações a serviços internos.
O papel de ferramentas como o Snyk Code na identificação e correção de vulnerabilidades
Ferramentas como o Snyk Code são essenciais para identificar e corrigir vulnerabilidades em aplicações Go. O Snyk Code analisa seu código-fonte em busca de problemas de segurança e oferece insights práticos e recomendações de correção. Ao integrar o Snyk ao seu fluxo de desenvolvimento, você pode detectar vulnerabilidades mais cedo, reduzir o risco de violações de segurança e manter uma base de código segura.
Exemplo em Go: detectar injeção de SQL com o Snyk Code
Considere o seguinte trecho de código Go, vulnerável a injeção de SQL:
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
_ "github.com/lib/pq"
)
func main() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
username := r.URL.Query().Get("username")
db, err := sql.Open("postgres", "user=youruser dbname=yourdb sslmode=disable")
if err != nil {
log.Fatal(err)
}
defer db.Close()
query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s'", username)
rows, err := db.Query(query)
if err != nil {
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
defer rows.Close()
// Process rows...
})
log.Fatal(http.ListenAndServe(":8080", nil))
}Neste exemplo, o parâmetro username é inserido diretamente na consulta SQL, deixando-a vulnerável a injeção de SQL. O Snyk Code pode detectar essa vulnerabilidade e sugerir o uso de consultas parametrizadas para evitá-la:
query := "SELECT * FROM users WHERE username = $1"
rows, err := db.Query(query, username)1. Validar e sanitizar entradas de usuário em Go
A validação e a sanitização de entradas são essenciais para proteger suas aplicações Go contra vetores de ataque comuns, como injeção de SQL e Cross-Site Scripting (XSS). Nunca confie em entradas de usuário, sejam elas parâmetros de consulta, corpos de respostas JSON de solicitações HTTP ou conteúdo de bancos de dados. Sempre valide as entradas de acordo com os esquemas esperados, aplique a lógica de sanitização quando necessário e codifique as saídas conforme o contexto.
Práticas recomendadas para validar entradas em Go
Ao lidar com entradas de usuário em Go, siga estas práticas recomendadas:
Defina esquemas de entrada: use esquemas rigorosos para definir entradas válidas. Bibliotecas como
go-playground/validatorpodem ajudar a aplicar essas regras.Sanitize as entradas: remova ou escape caracteres potencialmente nocivos das entradas de usuário. Por exemplo, codifique entradas em HTML para evitar XSS. Essa prática é útil principalmente quando implementada em ferramentas de mecanismos de template.
Use consultas parametrizadas: evite injeção de SQL usando consultas parametrizadas em vez de concatenar strings.
Codifique as saídas: codifique as saídas de acordo com o contexto, como em HTML para páginas da web ou em JSON para respostas de API.
Exemplo em Go: vinculação de parâmetros para evitar injeção de SQL
A injeção de SQL é uma vulnerabilidade grave de segurança no nível da aplicação, que ocorre quando entradas de usuário são incluídas diretamente em consultas SQL. Para evitá-la, use consultas parametrizadas:
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
func getUser(db *sql.DB, userID string) (User, error) {
var user User
err := db.QueryRow("SELECT id, name FROM users WHERE id = ?", userID).Scan(&user.ID, &user.Name)
if err != nil {
return user, err
}
return user, nil
}Neste exemplo, o marcador ? na consulta SQL é substituído com segurança pelo parâmetro userID, evitando ataques de injeção de SQL. Essa prática recomendada de segurança ajuda o mecanismo do banco de dados a diferenciar o significado original da consulta dos valores usados nela.
2. Como evitar o uso de subshell em Go
Em Go, o uso de subshell se refere à inicialização de subprocessos com funções como exec.CommandContext() ou exec.Command(). Embora essas funções ofereçam flexibilidade, elas também introduzem riscos significativos de segurança, especialmente vulnerabilidades de injeção de comandos. Isso ocorre quando um invasor manipula dados de entrada para executar comandos arbitrários no sistema host. Isso pode levar ao acesso não autorizado a dados, à corrupção de dados ou até mesmo ao comprometimento mais amplo do sistema.
Alternativas ao uso de subshell em Go: APIs de bibliotecas nativas, FFI etc.
Para reduzir os riscos associados ao uso de subshell, os desenvolvedores devem preferir as APIs nativas das bibliotecas de Go ou a Interface de Função Estrangeira (FFI) para realizar tarefas que, de outra forma, exigiriam comandos externos. A biblioteca padrão de Go é robusta e oferece vários recursos para manipulação de arquivos, processamento de imagens e muito mais, que muitas vezes podem substituir o uso de subshell.
Exemplo em Go: injeção de comandos na manipulação de imagens
Considere o exemplo em que uma entrada de usuário é usada para manipular um arquivo de imagem. Ele demonstra uma armadilha comum que pode levar à injeção de comandos:
package main
import (
"os/exec"
)
func manipulateImage(targetFilename string) error {
convertCmd := fmt.Sprintf("convert %s -resize %sx%s %s", targetFilename, fileSize, fileSize, targetFilename)
_, err = exec.CommandContext(ctx, "sh", "-c", convertCmd).CombinedOutput()
}
Neste exemplo, se targetFilename vier de uma entrada de usuário, um invasor poderá injetar comandos adicionais criando um nome de arquivo malicioso. Por exemplo, um nome como image.png; rm -rf /tmp poderia executar um comando destrutivo se não fosse sanitizado corretamente.
O risco aqui decorre do uso direto de entradas de usuário para montar comandos de shell e também do fato de o código não usar argumentos de comando preparados para compor o comando. Para reduzir esse risco, considere as seguintes estratégias:
Use bibliotecas nativas: em vez de depender de programas externos de linha de comando, use as bibliotecas nativas de Go.
Valide e sanitize as entradas: sempre valide e sanitize as entradas de usuário para garantir que sigam os padrões esperados. Isso pode impedir a execução de entradas maliciosas. O exemplo de código vulnerável deve evitar a interpolação direta no shell por meio de
sh -ce executar o comando diretamente. No entanto, isso também pode levar a vulnerabilidades de injeção de argumentos, que precisam ser devidamente sanitizados e escapados.
3. Como compor URLs com segurança
A necessidade de compor URLs seguras está diretamente relacionada a uma das 10 principais vulnerabilidades de segurança da OWASP: a falsificação de solicitação do lado do servidor (SSRF). SSRF é uma vulnerabilidade crítica que permite a um invasor manipular um servidor para enviar solicitações HTTP não pretendidas, por exemplo, a recursos internos, outros microsserviços privados ou qualquer outro serviço sem autorização. Isso pode levar ao acesso não autorizado a serviços internos, à exfiltração de dados ou até à execução remota de código. Para entender a SSRF em detalhes, consulte a lição sobre SSRF do Snyk Learn.
Estratégias para compor URLs com segurança em Go
Ao compor URLs em Go, especialmente quando há entradas de usuário envolvidas, é fundamental adotar estratégias de proteção contra SSRF. Estas são algumas práticas recomendadas:
Valide URLs: sempre valide URLs fornecidas pelo usuário com uma lista de permissões de domínios ou padrões verificados e aprovados. Isso garante que somente solicitações legítimas sejam feitas.
Use a análise de URLs: use o pacote
net/urlde Go para analisar e validar URLs. Isso ajuda a dividir a URL em componentes e verificar cada parte. Por exemplo, nesta etapa, você pode aplicar um controle de segurança para permitir somente um conjunto específico de esquemas de protocolo compatíveis (https://), bloqueandohttpe outros.Restrinja o acesso à rede interna: garanta que sua aplicação não faça solicitações a endereços IP internos ou endpoints sensíveis, usando sidecars do Kubernetes, proxies HTTP, gateways de API e outros controles de segurança que considerem a rede.
Defina timeouts e limites: embora não estejam diretamente relacionados à SSRF, configurar timeouts adequados e limites para o tamanho das respostas de solicitações de saída ajuda a evitar o esgotamento de recursos e a exfiltração de grandes volumes de dados, minimizando o impacto de uma SSRF.
Exemplo em Go: vulnerabilidade de SSRF causada por URLs fornecidas pelo usuário
O exemplo a seguir demonstra uma forma insegura de lidar com URLs fornecidas pelo usuário, que pode levar a uma vulnerabilidade de SSRF:
package main
import (
"net/http"
)
func fetchUserProfileImage(userURL string) (*http.Response, error) {
// Directly using user input to fetch remote data
resp, err := http.Get(userURL)
return resp, err
}Neste código, a função fetchUserProfileImage recebe uma URL do usuário e a usa diretamente em uma solicitação HTTP GET. Um invasor pode explorar isso para fazer solicitações a serviços internos.
Conclusão
Escrever código seguro em Go exige atenção constante e o cumprimento das práticas recomendadas. Estas são as principais medidas de segurança que abordamos para o desenvolvimento seguro em Go:
Valide e sanitize as entradas de usuário que percorrem os caminhos do código em Go.
Evite operações de subshell e prefira APIs nativas de Go e FFI sempre que disponíveis.
Trate URLs provenientes de entradas de usuário com extrema cautela para evitar vulnerabilidades de SSRF.
Use o Snyk Code para fazer análises de segurança contínuas
Dê o próximo passo para proteger suas aplicações Go. Crie uma conta gratuita na Snyk hoje mesmo e comece a integrar a segurança ao seu fluxo de desenvolvimento. Com a Snyk, você pode criar aplicações seguras com confiança e proteger seus usuários.
Para manter seu código Go seguro, use o Snyk Code. Ele realiza análises de segurança contínuas e ajuda você a identificar e corrigir vulnerabilidades na sua base de código antes que se tornem um problema.
Veja o Snyk Code em ação no meu IDE do VS Code: ele detecta problemas e usa um mecanismo de aprendizado de máquina para me ajudar com sugestões de como escrever código mais seguro em Go:

Participe do Fetch the Flag 2026!
Teste suas habilidades de segurança no nosso evento Capture the Flag, de 12 a 13 de fevereiro, das 12h às 12h (horário do leste dos EUA).