Broken Authentication

Broken Authentication and Go (Gin) [Apr 2026] [GHSA-h6c2-x2m2-mwhf]

[Updated Apr 2026] Updated GHSA-h6c2-x2m2-mwhf

Overview

Broken authentication vulnerabilities allow attackers to impersonate legitimate users, escalate privileges, or access sensitive data. In Go applications built with Gin, these issues often arise from weak session management, insecure cookies, or improperly signed tokens. In practice, attackers can abuse poorly implemented login flows, reuse tokens, or bypass password verification. Common manifestations include storing plaintext passwords, issuing tokens without verifying credentials, and placing tokens in cookies without HttpOnly or Secure attributes, which can lead to token theft via XSS or network sniffing on non-TLS channels. This guide explains how these problems appear in Go (Gin) apps and provides practical remediation. It covers strong password storage with bcrypt, robust JWT handling with issuer and audience validation, secure cookie attributes, TLS enforcement, and rate limiting on login endpoints. Note: No CVEs are provided here, but the patterns align with real-world authentication weaknesses and common patches described in general vulnerability advisories.

Code Fix Example

Go (Gin) API Security Remediation
package main\n\nimport (\n  \"net/http\"\n  \"time\"\n  \"github.com/gin-gonic/gin\"\n  \"github.com/golang-jwt/jwt/v4\"\n  \"golang.org/x/crypto/bcrypt\"\n  \"fmt\"\n)\n\ntype Credentials struct {\n  Username string\n  Password string\n}\n\nvar vulnUsers = map[string]string{\"alice\": \"password123\"}\nvar fixedUsers = map[string]string{}\nvar signingKey = []byte(\"secret\")\n\nfunc init() {\n  hash, _ := bcrypt.GenerateFromPassword([]byte(\"password123\"), bcrypt.DefaultCost)\n  fixedUsers[\"alice\"] = string(hash)\n}\n\nfunc loginVuln(c *gin.Context) {\n  var creds Credentials\n  if err := c.ShouldBindJSON(&creds); err != nil {\n    c.JSON(http.StatusBadRequest, gin.H{\"error\": \"invalid payload\"})\n    return\n  }\n  stored, ok := vulnUsers[creds.Username]\n  if !ok || stored != creds.Password {\n    c.JSON(http.StatusUnauthorized, gin.H{\"error\": \"invalid credentials\"})\n    return\n  }\n  claims := jwt.MapClaims{\n    \"sub\": creds.Username,\n    \"exp\": time.Now().Add(24*time.Hour).Unix(),\n  }\n  token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n  tokenString, _ := token.SignedString(signingKey)\n  // Insecure: cookie not HttpOnly and not Secure\n  c.SetCookie(\"token\", tokenString, 24*3600, \"/\", \"localhost\", false, false)\n  c.JSON(http.StatusOK, gin.H{\"status\": \"logged in (vuln)\"})\n}\n\nfunc loginFix(c *gin.Context) {\n  var creds Credentials\n  if err := c.ShouldBindJSON(&creds); err != nil {\n    c.JSON(http.StatusBadRequest, gin.H{\"error\": \"invalid payload\"})\n    return\n  }\n  storedHash, ok := fixedUsers[creds.Username]\n  if !ok || bcrypt.CompareHashAndPassword([]byte(storedHash), []byte(creds.Password)) != nil {\n    c.JSON(http.StatusUnauthorized, gin.H{\"error\": \"invalid credentials\"})\n    return\n  }\n  claims := jwt.MapClaims{\n     \"sub\": creds.Username,\n     \"exp\": time.Now().Add(24*time.Hour).Unix(),\n  }\n  token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n  tokenString, _ := token.SignedString(signingKey)\n  // Secure and HttpOnly cookie; ensure TLS in production\n  c.SetCookie(\"token\", tokenString, 24*3600, \"/\", \"localhost\", true, true)\n  c.JSON(http.StatusOK, gin.H{\"status\": \"logged in (fixed)\"})\n}\n\nfunc protected(c *gin.Context) {\n  c.JSON(http.StatusOK, gin.H{\"data\": \"secret\"})\n}\n\nfunc authMiddleware() gin.HandlerFunc {\n  return func(c *gin.Context) {\n    tokenString, err := c.Cookie(\"token\")\n    if err != nil {\n      c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{\"error\": \"unauthorized\"})\n      return\n    }\n    token, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {\n      if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {\n        return nil, fmt.Errorf(\"unexpected signing method: %v\", t.Header[\"alg\"])\n      }\n      return signingKey, nil\n    })\n    if err != nil || !token.Valid {\n      c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{\"error\": \"unauthorized\"})\n      return\n    }\n    c.Next()\n  }\n}\n\nfunc main() {\n  r := gin.Default()\n  r.POST(\"/login/vuln\", loginVuln)\n  r.POST(\"/login/fix\", loginFix)\n  r.GET(\"/protected\", authMiddleware(), protected)\n  r.Run(\":8080\")\n}

CVE References

Choose which optional cookies to allow. You can change this any time.