Skip to main content

Password change without identity check

Need

Enforce strong identity verification for password changes

Context

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

Description

Non compliant code

package main

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

type ChangePasswordForm struct {
NewPassword string `json:"new_password" binding:"required"`
}

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

r.POST("/change_password", func(c *gin.Context) {
var form ChangePasswordForm
if c.ShouldBindJSON(&form) == nil {
// Change password without verifying the old one
// TODO: add code to change the password in the database
c.JSON(http.StatusOK, gin.H{"status": "password changed"})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": "Bad request"})
}
})

r.Run()
}

The above Go code uses the Gin web framework to create a simple web server with a single /change_password endpoint. This endpoint accepts a POST request with a JSON body containing a new password.

The vulnerability lies in the /change_password endpoint. The endpoint allows any authenticated user to change their password without verifying their identity. The server does not ask for the old password or any other form of identity verification before changing the password.

This can be a serious security issue as an attacker who has somehow obtained a user's session could change the password without knowing the old one, effectively locking the original user out of their account.

The ChangePasswordForm struct is used to bind the incoming JSON to a Go variable. The NewPassword field is marked as required using Gin's binding tags. If the binding fails (for example, if the new_password field is missing in the request), the server responds with a 400 Bad Request error.

The actual code to change the password in the database is not included (marked with a TODO comment), as it's not relevant to the vulnerability.

This vulnerable code should be fixed by adding an identity verification step before changing the password. This could be asking for the old password, a two-factor authentication code, or another form of verification.

Steps

  • Implement an identity verification mechanism before allowing password change.
  • Require the user to provide their current password as part of the password change request.
  • Verify the provided current password against the one stored in the database.
  • If the current password is valid, proceed with changing the password.
  • If the current password is invalid, reject the password change request.
  • Consider implementing additional identity verification mechanisms such as two-factor authentication or email confirmation.

Compliant code

package main

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

type ChangePasswordForm struct {
OldPassword string `json:"old_password" binding:"required"`
NewPassword string `json:"new_password" binding:"required"`
}

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

r.POST("/change_password", func(c *gin.Context) {
var form ChangePasswordForm
if c.ShouldBindJSON(&form) == nil {
// TODO: add code to verify the old password with the one in the database
// If the old password is correct, change the password
// If the old password is incorrect, return an error
c.JSON(http.StatusOK, gin.H{"status": "password changed"})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": "Bad request"})
}
})

r.Run()
}

The updated code now includes an additional field in the ChangePasswordForm struct for the old password (OldPassword). This field is also required, just like the new password field (NewPassword).

In the POST request handler for "/change_password", the code now binds the JSON request body to the ChangePasswordForm struct. This means that the request must include both the old and new passwords.

The TODO comment indicates where you should add the code to verify the old password with the one stored in the database. If the old password is correct, you can proceed with changing the password. If the old password is incorrect, you should return an error.

This change ensures that the identity of the user is verified before changing the password, thus fixing the vulnerability. For additional security, consider implementing further identity verification mechanisms such as two-factor authentication or email confirmation.

References