Business information leak - JWT - Go

Business information leak - JWT - Go

Need

Protection of sensitive business information within JWT

Context

  • Usage of Go 1.16 for building efficient and scalable applications
  • Usage of Gin for building web applications in Go
  • Usage of jwt-go library 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"
)

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

    r.POST("/login", func(c *gin.Context) {
        username := c.PostForm("username")
        password := c.PostForm("password")

        token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
            "username": username,
            "password": password,
        })

        tokenString, _ := token.SignedString([]byte("secret"))

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

    r.Run()
}

        
        

In the code above, we have a simple Gin application that handles a POST request to the "/login" endpoint. The username and password are obtained from the form data of the incoming request. These details are then added to a JWT token as claims and the token is signed with a secret. The signed token is then returned in the response of the request.

The vulnerability in this code lies in the way the JWT token is created. JWT tokens are not encrypted, they are only base64 encoded which can easily be decoded. Therefore, storing sensitive information like username and password in the token can lead to information leakage if the token is intercepted or leaked.

Anyone who has the token can decode it and see the sensitive information. This is a serious security issue as it can lead to unauthorized access to the system if the username and password are compromised.

To fix this vulnerability, sensitive information like username and password should not be stored in the JWT token. Instead, non-sensitive information should be used, like user ID.

Steps

  • Do not include sensitive information such as username and password in the JWT payload.
  • Instead, use the JWT to store a unique identifier or reference to the user's session or account.
  • When the user logs in, generate a secure session ID or token and store it securely on the server.
  • Associate the session ID or token with the user's account or session data on the server.
  • Return the session ID or token to the client as the response to the login request.
  • Use the session ID or token for subsequent authenticated requests instead of including sensitive information in the JWT payload.
  • Ensure that the session ID or token is securely transmitted and stored on the client side.
  • Implement appropriate security measures such as encryption and secure storage to protect the session ID or token.
  • Validate the session ID or token on the server side for each authenticated request to ensure its authenticity and integrity.
  • Implement proper authorization checks to ensure that the user has the necessary permissions to access the requested resources.

Compliant code

        package main

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

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

    r.POST("/login", func(c *gin.Context) {
        username := c.PostForm("username")
        password := c.PostForm("password")

        // Validate the username and password
        // If valid, generate a unique session ID or token
        sessionID := generateSessionID(username, password)

        token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
            "sessionID": sessionID,
        })

        tokenString, _ := token.SignedString([]byte("secret"))

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

    r.Run()
}

func generateSessionID(username string, password string) string {
    // Implement the logic to generate a unique session ID or token
    // This could involve creating a hash of the username and password, or using a random number generator
    // The session ID or token should be stored securely on the server and associated with the user's account or session data
    return "uniqueSessionID"
}
        
        

The original code was vulnerable because it included sensitive information (username and password) in the JWT payload. This could potentially lead to a business information leak if the JWT was intercepted or decoded.

The fixed code no longer includes this sensitive information in the JWT payload. Instead, it generates a unique session ID or token when the user logs in. This session ID is then included in the JWT payload.

The generateSessionID function is a placeholder for the logic to generate a unique session ID or token. This could involve creating a hash of the username and password, or using a random number generator. The session ID or token should be stored securely on the server and associated with the user's account or session data.

The JWT, which now contains the session ID instead of the username and password, is then returned to the client as the response to the login request. This JWT can be used for subsequent authenticated requests.

This approach ensures that sensitive information is not included in the JWT payload, reducing the risk of a business information leak. It also allows for the implementation of additional security measures, such as encryption and secure storage of the session ID or token, and server-side validation of the session ID or token for each authenticated request.

References