Pourquoi l'authentification en Go n'est pas une plaisanterie ?
\n\nGo — un langage choisi pour sa rapidité et sa fiabilité. Mais même le serveur le plus rapide se transforme en citrouille si on peut y voler des mots de passe. L'authentification sécurisée dans les applications Go n'est pas simplement "j'ai installé une bibliothèque et j'ai oublié". C'est toute une science : comment stocker les mots de passe, comment ne pas échouer avec JWT, comment se protéger contre CSRF quand tout est déjà en feu.
\n\nDans cet article, nous ne ferons pas de cours théoriques. Nous écrirons du code. Du code réel, fonctionnel, que vous pouvez immédiatement intégrer dans votre projet. Nous aborderons les trois piliers de la sécurité : le hachage des mots de passe (bcrypt), le travail avec les jetons JWT et la protection contre la falsification de requêtes intersites (CSRF).
\n\nHacher les mots de passe correctement : pourquoi md5 est un mal
\n\nLa première étape vers la sécurité — ne jamais, vous m'entendez, jamais stocker les mots de passe en clair. Même si vous faites confiance à votre base de données. Une intrusion peut arriver à n'importe qui (demandez à Yahoo ou Adobe). Un hachage correct est votre bouclier.
\n\nEn Go, le choix standard est golang.org/x/crypto/bcrypt. Pourquoi bcrypt ? Il résiste aux attaques par force brute (inclut un "sel" et ralentit les calculs). MD5 ou SHA-1 se calculent en microsecondes. Bcrypt — en centaines de millisecondes. Pour un attaquant, c'est une catastrophe, pour l'utilisateur, c'est imperceptible.
Exemple : inscription d'un utilisateur
\n\npackage 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("Mot de passe :", password)\n fmt.Println("Hash : ", hash)\n\n match := checkPasswordHash(password, hash)\n fmt.Println("Correspond :", match)\n}\n\nNotez : bcrypt.DefaultCost correspond à 10 itérations. Pour la production, vous pouvez l'augmenter à 12-14, mais n'en faites pas trop, sinon le serveur s'effondrera sous la charge. Testez sur votre propre matériel.
Vérification du mot de passe lors de la connexion
\n\nTout est simple : on prend le mot de passe du formulaire, on prend le hash de la base de données, on compare avec bcrypt.CompareHashAndPassword. S'il retourne nil — l'utilisateur est légitime. Si erreur — on envoie un 401.
func loginHandler(w http.ResponseWriter, r *http.Request) {\n // on récupère le mot de passe du formulaire\n storedHash := getUserHashFromDB(r.FormValue("email"))\n if !checkPasswordHash(r.FormValue("password"), storedHash) {\n http.Error(w, "Mot de passe incorrect", http.StatusUnauthorized)\n return\n }\n // on crée une session ou un jeton\n}\n\nJWT : une authentification légère avec un piège
\n\nUne fois que l'utilisateur a saisi son mot de passe, il faut lui délivrer un "laissez-passer". L'une des méthodes populaires est JWT (JSON Web Token). C'est un jeton compact qui contient les données de l'utilisateur et une signature. Mais le diable est dans les détails : une implémentation incorrecte de JWT peut rendre votre application vulnérable.
\n\nGénération correcte de JWT en Go
\n\nUtilisez la bibliothèque github.com/golang-jwt/jwt/v5. N'écrivez jamais votre propre solution de cryptographie — uniquement des bibliothèques éprouvées.
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