Session Fixation - Go

Session Fixation - Go

Need

Prevention of session hijacking

Context

  • Usage of Go 1.16 for developing high-performance and concurrent applications
  • Usage of Gin framework for building web applications in Go

Description

Non compliant code

        package main

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

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

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

		// This is just a dummy check. Replace this with your actual user authentication logic
		if username == "admin" && password == "password" {
			c.SetCookie("session_id", "123456", 3600, "/", "localhost", false, true)
			c.JSON(200, gin.H{
				"message": "Logged in successfully",
			})
		} else {
			c.JSON(401, gin.H{
				"message": "Invalid credentials",
			})
		}
	})

	r.Run()
}
        
        

In the above code, when a user logs in with the correct username and password, a session cookie ( session_id) is set with a static value ( 123456). This cookie is used to maintain the user's session.

However, the issue here is that the session cookie's value does not change even after a successful login. This means that if an attacker knows the session cookie's value ( 123456 in this case), they can hijack the user's session by setting their own browser's cookie to this value.

This is a classic example of a Session Fixation vulnerability, where the attacker fixes the user's session ID before the user even logs in, and then uses that session ID to impersonate the user once they've logged in.

In a real-world scenario, the attacker would somehow have to make the victim use a session ID known to the attacker. After the user logs in, the attacker can use that session ID to impersonate the user.

This vulnerability can lead to unauthorized access to the user's account, and potentially, to sensitive data and functionality.

Steps

  • Generate a new session ID for each successful login
  • Invalidate the old session ID after generating a new one
  • Store the session ID securely, such as in a database or encrypted cookie
  • Implement session expiration to automatically log out users after a certain period of inactivity
  • Use secure cookies with the 'Secure' and 'HttpOnly' flags to prevent session hijacking
  • Implement strong user authentication and authorization mechanisms to prevent unauthorized access

Compliant code

        package main

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

func generateSessionID() string {
	b := make([]byte, 32)
	_, err := rand.Read(b)
	if err != nil {
		panic(err)
	}
	return base64.StdEncoding.EncodeToString(b)
}

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

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

		// This is just a dummy check. Replace this with your actual user authentication logic
		if username == "admin" && password == "password" {
			sessionID := generateSessionID()
			c.SetCookie("session_id", sessionID, 3600, "/", "localhost", false, true)
			c.JSON(200, gin.H{
				"message": "Logged in successfully",
			})
		} else {
			c.JSON(401, gin.H{
				"message": "Invalid credentials",
			})
		}
	})

	r.Run()
}
        
        

The updated code now generates a new session ID for each successful login, which helps to prevent session fixation attacks. The generateSessionID function uses the crypto/rand package to generate a random 32-byte value, which is then base64-encoded to create a session ID.

The session ID is stored in a secure cookie with the 'Secure' and 'HttpOnly' flags set to false and true respectively. The 'Secure' flag is set to false because the application is running on localhost, but in a production environment, this should be set to true to ensure that the cookie is only sent over HTTPS. The 'HttpOnly' flag is set to true to prevent the cookie from being accessed by client-side scripts, which helps to mitigate the risk of cross-site scripting (XSS) attacks.

The cookie also has an expiration time of 1 hour (3600 seconds), after which the user will be automatically logged out. This helps to limit the potential damage if an attacker is able to hijack a user's session.

Finally, the code includes a dummy user authentication check. In a real application, this should be replaced with a robust user authentication and authorization mechanism to prevent unauthorized access.

References