Insecure session management - CSRF Fixation - Go

Insecure session management - CSRF Fixation - Go

Need

Secure session management and protection against CSRF Fixation

Context

  • Usage of Go 1.16 for building efficient and scalable applications
  • Usage of gin-gonic/gin for building web applications in Go
  • Usage of net/http for building HTTP servers in a Node.js application

Description

Non compliant code

        package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.POST("/transaction", func(c *gin.Context) {
		publicKey := c.GetHeader("Authorization")
		if publicKey != "" {
			// process the transaction
		} else {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
		}
	})
	r.Run()
}
        
        

In the above code, the transaction endpoint is vulnerable to Cross-Site Request Forgery (CSRF) Fixation. The endpoint accepts a POST request and uses the Authorization header to identify the user.

The vulnerability lies in the fact that the Authorization header is a public key that remains the same for all payment links. This means an attacker can easily guess or obtain this key, create a malicious button that sends a POST request to the transaction endpoint with the same Authorization header, and trick a user into clicking it.

Once the user clicks the button, a transaction is initiated on their behalf without their knowledge or consent. The user then receives a push notification from the app and completes the transaction, thinking it was a legitimate request. This results in financial loss for the user and profit for the attacker.

Steps

  • Implement a secure session management mechanism to generate and manage unique session tokens for each user session.
  • Use a secure random number generator to generate session tokens that are difficult to guess.
  • Store the session tokens securely, such as in a secure HTTP-only cookie or in server-side storage.
  • Associate the session token with the user's session data, such as their authentication status and authorization level.
  • Verify the session token on each request to ensure that it is valid and associated with an authenticated user.
  • Use a CSRF token to protect against CSRF attacks. Generate a unique CSRF token for each user session and include it in forms or as a header in requests.
  • Validate the CSRF token on each request to ensure that it matches the one associated with the user's session.
  • Implement additional security measures, such as using HTTPS for secure communication and enforcing strong password policies.
  • Regularly update and patch the application and its dependencies to address any security vulnerabilities.

Compliant code

        package main

import (
	"crypto/rand"
	"encoding/base64"
	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
	"net/http"
)

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

	store := cookie.NewStore([]byte("secret"))
	r.Use(sessions.Sessions("mysession", store))

	r.POST("/transaction", func(c *gin.Context) {
		session := sessions.Default(c)
		publicKey := c.GetHeader("Authorization")
		csrfToken := session.Get("csrfToken")

		if publicKey != "" && csrfToken != nil && c.GetHeader("X-CSRF-Token") == csrfToken {
			// process the transaction
		} else {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
		}
	})

	r.GET("/login", func(c *gin.Context) {
		session := sessions.Default(c)
		csrfToken := generateCSRFToken()
		session.Set("csrfToken", csrfToken)
		session.Save()
	})

	r.Run()
}

func generateCSRFToken() string {
	b := make([]byte, 32)
	rand.Read(b)
	return base64.StdEncoding.EncodeToString(b)
}
        
        

The updated code now includes a secure session management mechanism using the github.com/gin-contrib/sessions package. This package provides a way to manage user sessions in a Gin application.

A new session store is created using a secret key and the Sessions middleware is added to the Gin engine. This middleware will manage the session data for each request.

In the /transaction route, the session is retrieved and the CSRF token is fetched from the session data. The CSRF token is then compared with the X-CSRF-Token header in the request. If the public key is not empty, the CSRF token is not nil, and the CSRF token matches the one in the request, the transaction is processed. Otherwise, an unauthorized status is returned.

A new /login route is added where a new CSRF token is generated and stored in the session data. This route simulates a login process where a new session and CSRF token would be created for the user.

The generateCSRFToken function generates a new CSRF token using a secure random number generator. The generated token is difficult to guess, providing protection against CSRF attacks.

This updated code provides a secure way to manage user sessions and protect against CSRF attacks by using unique session tokens and CSRF tokens. It also validates these tokens on each request to ensure they are valid and associated with an authenticated user.

References