Insecurely generated token - Lifespan - Go

Insecurely generated token - Lifespan - Go

Need

Secure token generation and management

Context

  • Usage of Go 1.16 for building high-performance and efficient applications
  • Usage of Gin for building web applications in Go
  • Usage of jwt-go for JSON Web Token (JWT) authentication and authorization

Description

Non compliant code

        package main

import (
	"github.com/gin-gonic/gin"
	"github.com/dgrijalva/jwt-go"
	"time"
)

func GenerateToken(c *gin.Context) {
	token := jwt.New(jwt.SigningMethodHS256)

	claims := token.Claims.(jwt.MapClaims)

	claims["authorized"] = true
	claims["user"] = "TestUser"
	claims["exp"] = time.Now().Add(time.Hour * 24 * 5).Unix()

	tokenString, _ := token.SignedString(jwtKey)

	c.JSON(200, gin.H{
		"token": tokenString,
	})
}

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

	r.GET("/token", GenerateToken)

	r.Run()
}
        
        

The above Go code uses the gin web framework and the JWT package to generate a new token. The GenerateToken function creates a new JWT token, adds some claims to it, and sets the expiration time to 5 days from the current time.

The claims["exp"] = time.Now().Add(time.Hour * 24 * 5).Unix() line is where the token's lifespan is set. It's set to expire 5 days from the current time, which is insecure because it gives an attacker a large window of opportunity to use a stolen token.

This is a significant security vulnerability because if a malicious user gets hold of a token, they can impersonate the legitimate user for up to 5 days. The longer a token is valid, the more time an attacker has to use it. Therefore, it's recommended to have short-lived tokens and implement token refresh mechanisms.

Steps

  • Reduce the lifespan of the session token to a shorter duration.
  • Determine an appropriate expiration time for the session token based on your application's requirements.
  • Update the 'exp' claim value in the token generation code to reflect the new expiration time.
  • Consider using a configuration file or environment variable to store the expiration time, allowing for easier modification in the future.
  • Test the updated code to ensure that the session tokens are generated with the correct expiration time.

Compliant code

        package main

import (
	"github.com/gin-gonic/gin"
	"github.com/dgrijalva/jwt-go"
	"time"
	"os"
	"strconv"
)

func GenerateToken(c *gin.Context) {
	token := jwt.New(jwt.SigningMethodHS256)

	claims := token.Claims.(jwt.MapClaims)

	claims["authorized"] = true
	claims["user"] = "TestUser"
	
	tokenLifeSpan, err := strconv.Atoi(os.Getenv("TOKEN_LIFESPAN"))
	if err != nil {
		// Default to 1 hour if environment variable is not set or cannot be parsed
		tokenLifeSpan = 1
	}

	claims["exp"] = time.Now().Add(time.Hour * time.Duration(tokenLifeSpan)).Unix()

	tokenString, _ := token.SignedString(jwtKey)

	c.JSON(200, gin.H{
		"token": tokenString,
	})
}

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

	r.GET("/token", GenerateToken)

	r.Run()
}
        
        

The updated code now generates session tokens with a lifespan that is determined by the TOKEN_LIFESPAN environment variable. This variable should be set to the desired lifespan of the token in hours. If the TOKEN_LIFESPAN environment variable is not set or cannot be parsed into an integer, the code defaults to a lifespan of 1 hour.

The exp claim of the token, which determines its expiration time, is now set to the current time plus the lifespan specified by TOKEN_LIFESPAN. This is done using the time.Now().Add(time.Hour * time.Duration(tokenLifeSpan)).Unix() line of code.

By using an environment variable to determine the token lifespan, the expiration time can be easily modified in the future without needing to change the code. This also allows for different expiration times to be used in different environments (e.g., shorter lifespans in a testing environment vs. longer lifespans in production).

After updating the code, it should be tested to ensure that the session tokens are generated with the correct expiration time.

References