Autenticação segura em Go: escrevendo código sem vulnerabilidades

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Por que autenticação em Go não é brincadeira?

\n\n

Go — uma linguagem escolhida por sua velocidade e confiabilidade. Mas até o servidor mais rápido se transforma em abóbora se for possível roubar senhas através dele. Autenticação segura em aplicações Go não é apenas "instalei uma biblioteca e esqueci". É toda uma ciência: como armazenar senhas, como não falhar com JWT, como se proteger contra CSRF quando tudo já está pegando fogo.

\n\n

Neste artigo, não vamos dar aulas teóricas. Vamos escrever código. Real, funcional, que pode ser inserido diretamente no seu projeto. Abordaremos os três pilares da segurança: hash de senhas (bcrypt), trabalho com tokens JWT e proteção contra falsificação de solicitação entre sites (CSRF).

\n\n

Fazendo hash de senhas corretamente: por que md5 é maligno

\n\n

O primeiro passo para a segurança — nunca, ouça bem, nunca armazene senhas em texto puro. Mesmo que você confie no seu banco de dados. Uma invasão pode acontecer com qualquer um (pergunte ao Yahoo ou Adobe). O hash correto é seu escudo.

\n\n

Em Go, a escolha padrão é golang.org/x/crypto/bcrypt. Por que bcrypt? Ele é resistente a ataques de força bruta (inclui "salt" e desacelera os cálculos). MD5 ou SHA-1 são calculados em microssegundos. Bcrypt — em centenas de milissegundos. Para um invasor, é um desastre; para o usuário, imperceptível.

\n\n

Exemplo: registro de usuário

\n\n
package main\n\nimport (\n    "fmt"\n    "golang.org/x/crypto/bcrypt"\n)\n\nfunc hashPassword(password string) (string, error) {\n    bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)\n    return string(bytes), err\n}\n\nfunc checkPasswordHash(password, hash string) bool {\n    err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))\n    return err == nil\n}\n\nfunc main() {\n    password := "mySecretPass123"\n    hash, _ := hashPassword(password)\n\n    fmt.Println("Senha:", password)\n    fmt.Println("Hash:  ", hash)\n\n    match := checkPasswordHash(password, hash)\n    fmt.Println("Corresponde:", match)\n}
\n\n

Observe: bcrypt.DefaultCost são 10 iterações. Para produção, pode aumentar para 12-14, mas não exagere, senão o servidor pode cair sob carga. Teste no seu hardware.

\n\n

Verificação de senha no login

\n\n

Simples: pegamos a senha do formulário, pegamos o hash do banco de dados, comparamos via bcrypt.CompareHashAndPassword. Se retornar nil — o usuário é legítimo. Se houver erro — enviamos 401.

\n\n
func loginHandler(w http.ResponseWriter, r *http.Request) {\n    // obtemos a senha do formulário\n    storedHash := getUserHashFromDB(r.FormValue("email"))\n    if !checkPasswordHash(r.FormValue("password"), storedHash) {\n        http.Error(w, "Senha inválida", http.StatusUnauthorized)\n        return\n    }\n    // criamos sessão ou token\n}
\n\n

JWT: autenticação leve com pegadinha

\n\n

Depois que o usuário inseriu a senha, é necessário emitir um "passe". Uma das formas populares é JWT (JSON Web Token). É um token compacto que contém dados do usuário e uma assinatura. Mas o diabo está nos detalhes: uma implementação incorreta de JWT pode tornar sua aplicação vulnerável.

\n\n

Geração correta de JWT em Go

\n\n

Usamos a biblioteca github.com/golang-jwt/jwt/v5. Nunca escreva sua própria criptografia — apenas bibliotecas testadas.

\n\n
import (\n    "time"\n    "github.com/golang-jwt/jwt/v5"\n)\n\nvar jwtKey = []byte("my_secret_key_which_must_be_long_and_random")\n\ntype Claims struct {\n    Username string `json:"username"`\n    jwt.RegisteredClaims\n}\n\nfunc generateToken(username string) (string, error) {\n    expirationTime := time.Now().Add(15 * time.Minute)\n    claims := &Claims{\n        Username: username,\n        RegisteredClaims: jwt.RegisteredClaims{\n            ExpiresAt: jwt.NewNumericDate(expirationTime),\n        },\n    }\n\n    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n    return token.SignedS                

Blogs

Book Recommendations