Password change without identity check - Scala

Password change without identity check - Scala

Need

Enforcement of identity verification for password changes

Context

  • Usage of Scala for building scalable and high-performance applications
  • Usage of play.api.mvc for handling HTTP requests and responses in Play Framework
  • Usage of play.api.data for handling form data in Play Framework
  • Usage of play.api.data.Forms for handling form data in Play Framework

Description

Non compliant code

        import play.api.mvc._
import play.api.data._
import play.api.data.Forms._

case class ChangePasswordForm(newPassword: String)

val changePasswordForm = Form(
  mapping(
    "newPassword" -> nonEmptyText
  )(ChangePasswordForm.apply)(ChangePasswordForm.unapply)
)

def changePassword = Action { implicit request =>
  val formValidationResult = changePasswordForm.bindFromRequest
  formValidationResult.fold({ formWithErrors =>
    // Handle form submission errors
    BadRequest
  }, { changePasswordData =>
    // Change password without checking the old one
    val newPassword = changePasswordData.newPassword
    // Assume we have a user service that changes the password
    userService.changePassword(newPassword)
    Ok("Password changed successfully")
  })
}
        
        

The above code snippet represents a password change functionality in a Scala backend application using the Play framework.

The ChangePasswordForm case class is a data model for the form data that will be submitted when a user attempts to change their password. It only contains one field, newPassword, which represents the new password that the user wants to set.

The changePasswordForm is a form that is mapped to the ChangePasswordForm case class. It only checks that the newPassword field is a non-empty string.

The changePassword function is an action that handles a POST request to change a user's password. It first binds the request data to the changePasswordForm. If the form data is invalid (e.g., the newPassword field is missing or empty), it returns a BadRequest response. If the form data is valid, it changes the user's password without checking the old password or verifying the user's identity in another way.

The vulnerability here is that the system allows a user to change their password without requesting the previous one or enforcing another identity verification mechanism. This can be exploited by an attacker who has gained temporary access to a user's account or session, allowing them to change the password and take over the account.

Steps

  • Implement an identity verification mechanism to ensure that the user is authorized to change their password.
  • Require the user to provide their current password as part of the password change process.
  • Verify the user's current password before allowing them to change their password.
  • Consider using additional identity verification methods such as two-factor authentication or email confirmation.
  • Ensure that the password change process is secure and protected from unauthorized access or tampering.

Compliant code

        import play.api.mvc._
import play.api.data._
import play.api.data.Forms._

case class ChangePasswordForm(oldPassword: String, newPassword: String)

val changePasswordForm = Form(
  mapping(
    "oldPassword" -> nonEmptyText,
    "newPassword" -> nonEmptyText
  )(ChangePasswordForm.apply)(ChangePasswordForm.unapply)
)

def changePassword = Action { implicit request =>
  val formValidationResult = changePasswordForm.bindFromRequest
  formValidationResult.fold({ formWithErrors =>
    // Handle form submission errors
    BadRequest
  }, { changePasswordData =>
    // Verify old password before changing to the new one
    val oldPassword = changePasswordData.oldPassword
    val newPassword = changePasswordData.newPassword
    // Assume we have a user service that verifies the old password and changes the password
    if (userService.verifyPassword(oldPassword)) {
      userService.changePassword(newPassword)
      Ok("Password changed successfully")
    } else {
      Unauthorized("Invalid old password")
    }
  })
}
        
        

The updated code now includes an identity verification mechanism to ensure that the user is authorized to change their password. This is done by requiring the user to provide their current password as part of the password change process.

The ChangePasswordForm now takes two parameters: oldPassword and newPassword. The form mapping has been updated to include oldPassword as a required field.

In the changePassword action, the old password is now extracted from the form data along with the new password. The old password is then verified using a hypothetical userService.verifyPassword method. If the old password is correct, the password is changed. If the old password is incorrect, an Unauthorized response is returned.

This change ensures that only the authorized user can change their password, thereby fixing the vulnerability. For additional security, consider implementing two-factor authentication or email confirmation.

References