Skip to main content

Como mitigar vulnerabilidades de SSRF em Go

Escrito por
GoLang Logo

10 de dezembro de 2024

0 minutos de leitura

Proteger requisições HTTP é fundamental ao desenvolver aplicações Go para evitar vulnerabilidades como Server-Side Request Forgery (SSRF). O SSRF ocorre quando um invasor manipula um servidor para fazer requisições não previstas, podendo acessar serviços internos ou dados confidenciais.

Vamos explorar como proteger requisições HTTP usando técnicas de análise e validação de URLs e apresentar um exemplo de código para reforçar a segurança do handler de requisições HTTP GET http.Get.

O código do handler de rota HTTP que envia requisições HTTP para o próprio domínio do usuário para buscar uma imagem é o seguinte (simplificado para ser mais breve), em uma função chamada downloadAndResize:

func downloadAndResize(tenantID, fileID, fileSize string) error {
	// ...

	downloadResp, err := http.Get(info.Download)
	if (err != nil) {
		panic(err)
	}

	// ...
}

Como mitigar vulnerabilidades de SSRF em código Go

Para mitigar vulnerabilidades de SSRF em aplicações Go, os desenvolvedores devem se concentrar em validar e higienizar as entradas e entender como construir URLs com segurança, sem manipular o domínio resultante.

Ataques de SSRF frequentemente exploram a validação insuficiente de entradas, permitindo que invasores manipulem URLs e redirecionem requisições para destinos não previstos. Com a implementação de uma validação e higienização robustas das entradas, os desenvolvedores podem reduzir significativamente o risco de ataques de SSRF.

Como validar as entradas de tenantID e fileID

Na nossa aplicação Go vulnerável, tenantID e fileID são extraídos da string de consulta sem nenhuma validação. Essa falta de validação abre caminho para ataques de SSRF.

Veja o código Go a seguir, que implementa a validação de entradas para garantir que esses parâmetros possam ser usados com segurança:

func isValidTenantID(tenantID string) bool {
	// Implement a regex pattern to validate tenantID format
	// Example: only allow alphanumeric characters
	validTenantIDPattern := `^[a-zA-Z0-9]+$`
	matched, _ := regexp.MatchString(validTenantIDPattern, tenantID)
	return matched
}

func isValidFileID(fileID string) bool {
	// Implement a regex pattern to validate fileID format
	// Example: only allow alphanumeric characters and hyphens
	validFileIDPattern := `^[a-zA-Z0-9-]+$`
	matched, _ := regexp.MatchString(validFileIDPattern, fileID)
	return matched
}

Por que restringir requisições de saída a hosts confiáveis

Outra estratégia eficaz para mitigar vulnerabilidades de SSRF é restringir as requisições de saída a hosts confiáveis. Isso pode ser feito mantendo uma lista de permissões de hosts e verificando se o host de destino de cada requisição está nela.

Veja um exemplo de como implementar a restrição de hosts na função downloadAndResize:

func isTrustedHost(host string) bool {
	// Define a list of trusted hosts
	trustedHosts := []string{"localtest.me", "example.com"}
	for _, trustedHost := range trustedHosts {
		if host == trustedHost {
			return true
		}
	}
	return false
}

func downloadAndResize(tenantID, fileID, fileSize string) error {
	urlStr := fmt.Sprintf("http://%s.%s/storage/%s.json", tenantID, baseHost, fileID)
	parsedURL, err := url.Parse(urlStr)
	if err != nil {
		panic(err)
	}

	if !isTrustedHost(parsedURL.Hostname()) {
		return fmt.Errorf("untrusted host: %s", parsedURL.Hostname())
	}

	// Proceed with the rest of the function
	// ...
}

Ao implementar restrições de hosts permitidos como controle de segurança, garantimos que a aplicação Go envie requisições HTTP somente para uma lista fechada de hosts confiáveis e predefinidos, reduzindo ainda mais o impacto de um ataque de SSRF.

Por que adotar uma abordagem de segurança em primeiro lugar no desenvolvimento

Adotar uma abordagem de segurança em primeiro lugar é fundamental para que os desenvolvedores criem aplicações robustas e seguras — e com Go não é diferente. Isso envolve incorporar considerações de segurança a cada etapa do ciclo de vida do desenvolvimento de software, do design à implantação.

Ao priorizar a segurança, os desenvolvedores podem:

  • Reduzir o risco de violações de segurança e vazamentos de dados.

  • Proteger os dados dos usuários e manter a confiança.

  • Cumprir as regulamentações e os padrões do setor.

  • Minimizar o custo e o impacto de incidentes de segurança.

Ao seguir essas práticas recomendadas e aproveitar recursos como a folha de dicas de segurança para Go, os desenvolvedores podem reforçar a segurança de suas aplicações Go e se proteger contra ameaças como SSRF.

Como usar o Snyk Code para reforçar a segurança

Aprendemos como se proteger contra vulnerabilidades de SSRF e por que os desenvolvedores devem validar e higienizar as entradas dos usuários, restringir requisições de saída a hosts confiáveis e usar listas de permissões para controlar quais domínios podem ser acessados. Além disso, ferramentas de segurança como o Snyk Code podem ajudar a identificar e corrigir essas vulnerabilidades no início do processo de desenvolvimento.

Para reforçar ainda mais a segurança da sua aplicação, considere usar Snyk Code para análise estática. O Snyk Code pode identificar vulnerabilidades de SSRF e outros problemas de segurança no seu código Go antes da implantação. Ao integrar o Snyk ao seu IDE ou repositório, você identifica vulnerabilidades mais cedo e mantém sua aplicação segura.

Para conhecer mais práticas recomendadas de segurança para Go, confira a folha de dicas de segurança para Go e aprenda a conteinerizar suas aplicações com segurança usando Docker.

Ao implementar essas técnicas e usar ferramentas como o Snyk, você protege suas aplicações Go contra ataques de SSRF e outras ameaças à segurança.

O Snyk pode ser usado com outras linguagens de programação?

O Snyk é uma ferramenta versátil que oferece suporte a uma ampla variedade de linguagens de programação, sendo essencial para desenvolvedores que trabalham com diferentes tecnologias. Seja para desenvolver em JavaScript, Python, Java, Ruby, PHP ou outras linguagens, o Snyk oferece soluções de segurança abrangentes e adaptadas a cada ecossistema.

Isso inclui:

  • JavaScript e TypeScript: com o Snyk, você pode analisar suas aplicações Node.js em busca de vulnerabilidades no código e nas dependências.

  • Python: o Snyk ajuda a identificar vulnerabilidades em pacotes Python e oferece recomendações práticas para corrigi-las.

  • Java: a integração do Snyk com Maven e Gradle permite que desenvolvedores Java protejam suas aplicações, identificando e corrigindo vulnerabilidades nas dependências.

  • Ruby: desenvolvedores Ruby podem usar o Snyk para analisar seus arquivos Gemfile e Gemfile.lock em busca de vulnerabilidades conhecidas.

PHP: o Snyk oferece suporte ao Composer, permitindo que desenvolvedores PHP protejam seus projetos ao identificar vulnerabilidades nas dependências.

Prepare-se para o futuro dos assistentes de programação com a Snyk

Conheça os assistentes de programação e saiba como integrar essas ferramentas aos fluxos de trabalho de desenvolvimento.