Broken Authentication

Broken Authentication in Go (Gin) Remediation Guide [GHSA-q485-cg9q-xq2r]

[Updated March 2026] Updated GHSA-q485-cg9q-xq2r

Overview

Broken authentication in Go Gin apps enables attackers to impersonate users, hijack sessions, or bypass login controls. In practice, attackers may exploit predictable tokens, insecure cookies, or misused JWTs to access protected resources without credentials. If tokens are not time bound, not tied to a single device, or exposed to the browser, an attacker can reuse them later with little difficulty. Go Gin patterns that contribute include generating tokens using user input or a static secret, setting cookies without HttpOnly or Secure flags, and storing session data in memory without expiry or revocation. Middlewares that accept any token or rely on a header alone without binding to a session or IP can be abused. Without CSRF protection for cookie based tokens, state changing requests become vulnerable. Impact includes account takeover, data exposure, and privilege escalation. In addition, insecure defaults reduce the ability to revoke sessions and rotate credentials. Attackers can leverage automated tooling to enumerate tokens and reuse them until a user logs out or token expiry. Remediation approach includes enabling cryptographically secure tokens, setting HttpOnly and Secure on cookies, using short lived tokens with rotation, storing sessions server side or using signed tokens with exp claims, protecting against CSRF, enforcing TLS, and supporting MFA.

Code Fix Example

Go (Gin) API Security Remediation
package main

import (
  "crypto/rand"
  "encoding/hex"
  "net/http"
  "time"

  "github.com/gin-gonic/gin"
)

type Session struct {
  Username string
  Expiry   time.Time
}

var vulnSessions = map[string]string{}
var fixSessions = map[string]Session{}

func main() {
  r := gin.Default()

  // Vulnerable login and protected endpoint (predictable tokens, insecure cookies)
  r.POST("/vuln/login", func(c *gin.Context) {
    var req struct {
      Username string `json:"username"`
      Password string `json:"password"`
    }
    if err := c.BindJSON(&req); err != nil {
      c.Status(http.StatusBadRequest)
      return
    }
    // Insecure credential check (for demonstration only)
    if req.Username == "alice" && req.Password == "alicepass" {
      token := "token_" + req.Username // predictable token
      vulnSessions[token] = req.Username
      c.SetCookie("AuthToken", token, 3600, "/", "", false, false) // HttpOnly=false; Secure=false
      c.JSON(http.StatusOK, gin.H{"token": token})
      return
    }
    c.Status(http.StatusUnauthorized)
  })

  r.GET("/vuln/protected", func(c *gin.Context) {
    t, err := c.Cookie("AuthToken")
    if err != nil {
      c.Status(http.StatusUnauthorized)
      return
    }
    if user, ok := vulnSessions[t]; ok {
      c.JSON(http.StatusOK, gin.H{"user": user})
      return
    }
    c.Status(http.StatusUnauthorized)
  })

  // Fixed login and protected endpoint (token rotation, HttpOnly cookies, expiry)
  r.POST("/fix/login", func(c *gin.Context) {
    var req struct {
      Username string `json:"username"`
      Password string `json:"password"`
    }
    if err := c.BindJSON(&req); err != nil {
      c.Status(http.StatusBadRequest)
      return
    }
    if req.Username == "alice" && req.Password == "alicepass" {
      // Crypto random token
      b := make([]byte, 32)
      if _, err := rand.Read(b); err != nil {
        c.Status(http.StatusInternalServerError)
        return
      }
      token := hex.EncodeToString(b)
      fixSessions[token] = Session{Username: req.Username, Expiry: time.Now().Add(15 * time.Minute)}
      c.SetCookie("AuthToken", token, 15*60, "/", "", true, true) // HttpOnly; Secure
      c.JSON(http.StatusOK, gin.H{"token": token})
      return
    }
    c.Status(http.StatusUnauthorized)
  })

  r.GET("/fix/protected", func(c *gin.Context) {
    t, err := c.Cookie("AuthToken")
    if err != nil {
      c.Status(http.StatusUnauthorized)
      return
    }
    if s, ok := fixSessions[t]; ok {
      if time.Now().Before(s.Expiry) {
        c.JSON(http.StatusOK, gin.H{"user": s.Username})
        return
      }
      delete(fixSessions, t)
    }
    c.Status(http.StatusUnauthorized)
  })

  r.Run(":8080")
}

CVE References

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