Cross-site request forgery - Go

Cross-site request forgery - Go

Need

Protection against Cross-Site Request Forgery (CSRF) attacks

Context

  • Usage of Go 1.16 for building high-performance and concurrent applications
  • Usage of gin-gonic/gin for building web applications in Go

Description

Non compliant code

        package main

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

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

	r.POST("/changePassword", func(c *gin.Context) {
		// Assume the user is authenticated
		// Get the new password from the request body
		newPassword := c.PostForm("newPassword")
		// Change the user's password
		// ...
		c.JSON(200, gin.H{
			"status": "Password changed successfully",
		})
	})

	r.Run() // listen and serve on 0.0.0.0:8080
}
        
        

This is a simple Go backend application using the Gin framework. It has a single POST endpoint /changePassword which accepts a new password from the request body and changes the user's password.

The vulnerability here is that there is no CSRF (Cross-Site Request Forgery) protection. If an authenticated user visits a malicious website, that website could send a POST request to /changePassword without the user's knowledge or consent, effectively changing the user's password.

The application assumes that if a request is made to the /changePassword endpoint, it must have come from an authenticated and authorized user. However, there are no checks in place to verify that the request was intentionally made by the user. This is the essence of a CSRF vulnerability.

To exploit this vulnerability, an attacker could create a website that sends a POST request to /changePassword when visited. If an authenticated user visits this website, their password would be changed without their knowledge or consent.

Steps

  • Implement Cross-Site Request Forgery (CSRF) protection in the application.
  • Generate and include a CSRF token in all forms and requests that modify sensitive data.
  • Verify the CSRF token on the server-side for every request that modifies sensitive data.
  • Ensure that the CSRF token is unique for each user session and is not predictable.
  • Use secure HTTP methods (e.g., POST) for actions that modify sensitive data.
  • Include the CSRF token in the request headers or as a hidden field in HTML forms.
  • Validate the CSRF token on the server-side before processing the request.
  • Consider implementing SameSite cookies to further protect against CSRF attacks.

Compliant code

        package main

import (
	"github.com/gin-gonic/gin"
	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
)

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

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

	r.GET("/form", func(c *gin.Context) {
		session := sessions.Default(c)
		token := session.Get("csrf_token")
		if token == nil {
			token = generateCSRFToken() // function to generate a new token
			session.Set("csrf_token", token)
			session.Save()
		}
		c.HTML(200, "form.html", gin.H{"csrf_token": token})
	})

	r.POST("/changePassword", func(c *gin.Context) {
		session := sessions.Default(c)
		token := session.Get("csrf_token")
		if token == nil || token != c.PostForm("csrf_token") {
			c.JSON(403, gin.H{"message": "CSRF token mismatch"})
			return
		}
		newPassword := c.PostForm("newPassword")
		// Change the user's password
		// ...
		c.JSON(200, gin.H{
			"status": "Password changed successfully",
		})
	})

	r.Run() // listen and serve on 0.0.0.0:8080
}

func generateCSRFToken() string {
	// Implement your CSRF token generation logic here
	// This is just a placeholder
	return "csrf_token"
}
        
        

The above code fixes the Cross-Site Request Forgery (CSRF) vulnerability in the Go application using the Gin framework.

The application now generates a CSRF token for each user session. This token is stored in the user's session and is also included as a hidden field in the HTML form that is used to change the password.

When the form is submitted, the server checks that the CSRF token in the form matches the CSRF token in the user's session. If the tokens do not match or if the token is missing, the server responds with a 403 Forbidden status code and does not process the request.

This prevents an attacker from tricking an authenticated user into executing actions without their consent, as the attacker would not have access to the CSRF token.

The application uses the github.com/gin-contrib/sessions package to manage sessions and the github.com/gin-contrib/sessions/cookie package to store session data in secure, encrypted cookies.

The generateCSRFToken function is a placeholder and should be replaced with a function that generates a secure, random CSRF token.

The application also uses the POST HTTP method for the /changePassword endpoint, which is a best practice for actions that modify sensitive data.

Finally, the application could further protect against CSRF attacks by implementing SameSite cookies, which are not included in cross-site requests.

References