Authentification sécurisée en Go : écrire du code sans vulnérabilités

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Pourquoi l'authentification en Go n'est pas une plaisanterie ?

\n\n

Go — 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\n

Dans 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\n

Hacher les mots de passe correctement : pourquoi md5 est un mal

\n\n

La 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\n

En 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.

\n\n

Exemple : inscription d'un utilisateur

\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("Mot de passe :", password)\n    fmt.Println("Hash :  ", hash)\n\n    match := checkPasswordHash(password, hash)\n    fmt.Println("Correspond :", match)\n}
\n\n

Notez : 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.

\n\n

Vérification du mot de passe lors de la connexion

\n\n

Tout 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.

\n\n
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\n

JWT : une authentification légère avec un piège

\n\n

Une 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\n

Génération correcte de JWT en Go

\n\n

Utilisez la bibliothèque github.com/golang-jwt/jwt/v5. N'écrivez jamais votre propre solution de cryptographie — uniquement des bibliothèques éprouvées.

\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