Segurança: prevenção de header injection e CORS
1. Introdução à Segurança de Headers HTTP em Go
Headers HTTP são metadados que acompanham requisições e respostas, controlando caching, autenticação, tipo de conteúdo e muito mais. Por serem processados por servidores e navegadores, representam um vetor de ataque significativo quando manipulados indevidamente.
Três conceitos fundamentais precisam ser compreendidos:
- Header Injection: inserção de caracteres de controle (
\r\n) para adicionar headers maliciosos - Response Splitting: técnica que divide a resposta HTTP em duas, envenenando caches
- Cache Poisoning: contaminação de proxies para servir conteúdo malicioso a outros usuários
Em Go, o pacote net/http oferece controle direto sobre headers, mas exige cuidado. Diferentemente de linguagens como PHP, Go não sanitiza automaticamente valores de headers — a responsabilidade é do desenvolvedor.
2. Header Injection: Mecanismos e Riscos
A injeção ocorre quando o servidor reflete dados do usuário em headers HTTP sem validação. O atacante insere sequências como \r\n (CRLF) para encerrar o header atual e iniciar novos.
Consequências graves incluem:
- Sequestro de sessão via cookies injetados
- Redirecionamento forçado para sites maliciosos
- Contaminação de cache em proxies intermediários
Exemplo vulnerável em Go:
package main
import (
"fmt"
"net/http"
)
func vulneravelHandler(w http.ResponseWriter, r *http.Request) {
// PERIGO: reflete input do usuário diretamente no header
nome := r.URL.Query().Get("nome")
w.Header().Set("X-Custom-Header", "Olá, "+nome)
fmt.Fprintf(w, "Header definido!")
}
func main() {
http.HandleFunc("/", vulneravelHandler)
http.ListenAndServe(":8080", nil)
}
Uma requisição como /?nome=João%0d%0aSet-Cookie:%20session=hacked injetaria um cookie malicioso.
3. Prevenção de Header Injection em Go
Validação rigorosa de entrada é a primeira linha de defesa:
import (
"net/http"
"strings"
)
func sanitizarHeader(valor string) string {
// Remove caracteres CR (\r) e LF (\n)
valor = strings.ReplaceAll(valor, "\r", "")
valor = strings.ReplaceAll(valor, "\n", "")
return valor
}
func handlerSeguro(w http.ResponseWriter, r *http.Request) {
nome := sanitizarHeader(r.URL.Query().Get("nome"))
w.Header().Set("X-Custom-Header", nome)
w.WriteHeader(http.StatusOK)
}
Diferença crucial: http.Header.Set sobrescreve valores existentes, enquanto http.Header.Add pode acumular múltiplos valores — use Set para evitar duplicação maliciosa.
Middleware de proteção global:
func SecurityHeadersMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Remove headers potencialmente perigosos da requisição
r.Header.Del("X-Forwarded-For")
r.Header.Del("X-Real-IP")
next.ServeHTTP(w, r)
})
}
Nunca confie em headers do cliente para decisões críticas como autenticação — sempre valide no backend.
4. CORS (Cross-Origin Resource Sharing) – Fundamentos
CORS é um mecanismo de segurança do navegador que controla requisições entre diferentes origens (domínio, protocolo, porta). Sem CORS, um site malicioso não poderia ler respostas de APIs de terceiros.
Headers principais:
- Access-Control-Allow-Origin: origens permitidas
- Access-Control-Allow-Methods: métodos HTTP autorizados
- Access-Control-Allow-Headers: headers personalizados
- Access-Control-Allow-Credentials: suporte a cookies/autenticação
Requisições preflight (OPTIONS) ocorrem quando:
- Método diferente de GET/POST/HEAD
- Headers não considerados "simples"
- Cookies ou credenciais envolvidas
5. Implementando CORS em Go: Abordagens Seguras
Configuração manual:
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origem := r.Header.Get("Origin")
// Validação dinâmica de origem
if strings.HasSuffix(origem, ".meudominio.com") {
w.Header().Set("Access-Control-Allow-Origin", origem)
} else {
w.Header().Set("Access-Control-Allow-Origin", "https://app.meudominio.com")
}
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
w.Header().Set("Access-Control-Allow-Credentials", "true")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusNoContent)
return
}
next.ServeHTTP(w, r)
})
}
Usando biblioteca rs/cors:
import "github.com/rs/cors"
func main() {
c := cors.New(cors.Options{
AllowedOrigins: []string{"https://app.meudominio.com"},
AllowedMethods: []string{"GET", "POST", "PUT"},
AllowedHeaders: []string{"Content-Type", "Authorization"},
AllowCredentials: true,
MaxAge: 86400,
})
handler := c.Handler(http.HandlerFunc(meuHandler))
http.ListenAndServe(":8080", handler)
}
Armadilhas comuns:
- Usar * em produção — permite qualquer origem
- Expor headers sensíveis como Authorization sem necessidade
- Configurar AllowCredentials: true com * — inválido por especificação
6. Headers de Segurança Adicionais (Defesa em Profundidade)
Além de CORS, headers de segurança fortalecem a proteção:
func securityHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("Content-Security-Policy", "default-src 'self'")
w.Header().Set("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
w.Header().Set("X-XSS-Protection", "1; mode=block")
next.ServeHTTP(w, r)
})
}
X-Content-Type-Options: impede MIME sniffingX-Frame-Options: bloqueia clickjackingContent-Security-Policy: controla fontes de conteúdoStrict-Transport-Security: força HTTPSX-XSS-Protection: ativa filtro XSS do navegador
7. Testando e Auditando a Segurança de Headers
Teste manual com curl:
curl -I https://api.meudominio.com
Testes automatizados em Go:
func TestSecurityHeaders(t *testing.T) {
handler := securityHeaders(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
server := httptest.NewServer(handler)
defer server.Close()
resp, _ := http.Get(server.URL)
assert.Equal(t, "nosniff", resp.Header.Get("X-Content-Type-Options"))
assert.Equal(t, "DENY", resp.Header.Get("X-Frame-Options"))
}
Ferramentas como OWASP ZAP e SecLists ajudam a validar configurações em produção.
8. Conclusão e Checklist de Segurança
Headers HTTP são uma superfície de ataque crítica. Em Go, a prevenção de header injection e a configuração correta de CORS exigem atenção constante.
Checklist para deploy seguro:
- [ ] Validar e sanitizar todo input refletido em headers
- [ ] Usar http.Header.Set em vez de Add
- [ ] Configurar CORS com origens específicas, nunca *
- [ ] Adicionar headers de segurança (CSP, HSTS, X-Frame-Options)
- [ ] Testar configurações com httptest e scanners OWASP
- [ ] Revisar periodicamente as políticas de CORS
A segurança é um processo contínuo — mantenha-se atualizado com as melhores práticas e vulnerabilidades emergentes.
Referências
- net/http - Documentação oficial do Go — Referência completa do pacote HTTP padrão, incluindo manipulação de headers
- OWASP HTTP Response Splitting — Descrição detalhada do ataque e técnicas de mitigação
- MDN Web Docs: CORS — Guia completo sobre CORS, headers e fluxo de requisições
- rs/cors - Biblioteca Go para CORS — Middleware CORS flexível e seguro para Go
- OWASP Secure Headers Project — Lista completa de headers de segurança e recomendações de configuração
- RFC 7230 - HTTP/1.1 Message Syntax and Routing — Especificação oficial sobre formatação de mensagens HTTP e prevenção de injection
- Go Security - Best Practices — Diretrizes oficiais da equipe Go sobre segurança em aplicações