Skip to main content

Password reset poisoning

Need

Secure password reset functionality

Context

  • Usage of Scala for building scalable and functional 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
  • Usage of views.html.resetPassword for rendering the reset password HTML template

Description

Non compliant code

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

case class PasswordResetForm(email: String)

val passwordResetForm = Form(
mapping(
"email" -> email
)(PasswordResetForm.apply)(PasswordResetForm.unapply)
)

def sendPasswordResetLink = Action { implicit request =>
passwordResetForm.bindFromRequest.fold(
formWithErrors => {
// handle errors
},
passwordResetForm => {
val resetLink = routes.Application.resetPassword(passwordResetForm.email).absoluteURL()
EmailService.send(passwordResetForm.email, resetLink)
Ok("Password reset link has been sent to your email.")
}
)
}

def resetPassword(email: String) = Action {
Ok(views.html.resetPassword(email))
}

The above Scala code uses the Play framework to handle password reset requests. The sendPasswordResetLink function is the endpoint that is used to initiate the password reset process. It takes a form submission with an email address, generates a password reset link using the absoluteURL function, and sends the link to the user's email address.

The vulnerability lies in the absoluteURL function. This function generates an absolute URL based on the current request's host header. However, an attacker can manipulate the host header to point to a domain under their control. When the absoluteURL function generates the password reset link, it will use the malicious host, resulting in a password reset link that points to the attacker's domain.

If the user clicks on the link and enters their new password, the attacker can capture this information and gain unauthorized access to the user's account. This type of attack is known as password reset poisoning.

Steps

  • Validate the email address before generating the password reset link.
  • Use a secure method to generate the password reset link, such as using a random token or a secure hash.
  • Ensure that the generated password reset link includes the correct domain and is not susceptible to manipulation by an attacker.
  • Implement proper input validation and sanitization to prevent any malicious input from being processed.
  • Use HTTPS for all communication related to password reset to ensure the confidentiality and integrity of the reset process.
  • Implement rate limiting or other security measures to prevent abuse of the password reset functionality.
  • Regularly update and patch the Play framework and any dependencies to ensure that any security vulnerabilities are addressed.

Compliant code

import play.api.mvc._
import play.api.data._
import play.api.data.Forms._
import java.util.UUID

case class PasswordResetForm(email: String)

val passwordResetForm = Form(
mapping(
"email" -> email.verifying("Invalid email address", e => e.contains("@"))
)(PasswordResetForm.apply)(PasswordResetForm.unapply)
)

def sendPasswordResetLink = Action { implicit request =>
passwordResetForm.bindFromRequest.fold(
formWithErrors => {
// handle errors
},
passwordResetForm => {
val token = UUID.randomUUID().toString
val resetLink = routes.Application.resetPassword(token).absoluteURL(secure = true)
EmailService.send(passwordResetForm.email, resetLink)
Ok("Password reset link has been sent to your email.")
}
)
}

def resetPassword(token: String) = Action {
// Verify the token before resetting the password
Ok(views.html.resetPassword(token))
}

The updated code includes several changes to address the password reset poisoning vulnerability:

  1. Email Validation: The email address is validated to ensure it is in the correct format. This is done using the verifying method on the email field in the form mapping.

  2. Secure Reset Link Generation: A secure, random token is generated using UUID.randomUUID().toString. This token is used as the parameter for the resetPassword route instead of the email address. This prevents an attacker from manipulating the email parameter in the reset link.

  3. Secure Communication: The absoluteURL method is called with the secure parameter set to true. This ensures that the reset link is generated with the HTTPS protocol, ensuring the confidentiality and integrity of the reset process.

  4. Token Verification: In the resetPassword action, the provided token should be verified before allowing the password to be reset. This could involve checking the token against a list of valid tokens stored in the database, for example.

These changes help to ensure that the password reset process is secure and not susceptible to password reset poisoning attacks.

References