Enforce strong identity verification for password changes
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.
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.