Skip to main content

Authentication mechanism absence or evasion - OTP

Need

Implementation of a secure and validated One-Time Password (OTP) authentication mechanism

Context

  • Usage of Scala for functional and object-oriented programming in the JVM environment
  • Usage of play.api.mvc for handling HTTP requests and responses in Play Framework

Description

Non compliant code

import play.api.mvc._

object UserController extends Controller {
def register = Action { implicit request =>
val body: AnyContent = request.body
val jsonBody: Option[JsValue] = body.asJson

jsonBody.map { json =>
(json \\ "phoneNumber").asOpt[String].map { phoneNumber =>
(json \\ "otp").asOpt[String].map { otp =>
// Here we are just checking if the otp is not empty but not validating it
if (otp.nonEmpty) {
// Registration process
} else {
BadRequest("Missing OTP")
}
}.getOrElse {
BadRequest("Missing OTP")
}
}.getOrElse {
BadRequest("Missing phone number")
}
}.getOrElse {
BadRequest("Expecting application/json request body")
}
}
}

The above code represents the registration process in a Scala application using the Play framework.

The method register is an action that expects a JSON request body with a phoneNumber and an otp (One-Time Password).

The vulnerability lies in the fact that the OTP is not being validated. The code simply checks if the OTP is not empty (if (otp.nonEmpty)), but it does not validate if the OTP is the correct one that was sent to the user's phone number.

This means that an attacker can provide any non-empty string as the OTP, and the system will accept it, thereby bypassing the OTP verification process. This is a serious security vulnerability as it allows an attacker to register an account with any phone number, even if they don't have access to the OTP sent to that number.

Additionally, the code does not check if the OTP complies with the specified number of digits (6 in this case). This can lead to further vulnerabilities as an attacker may be able to exploit this to bypass the OTP verification process.

Steps

  • Implement a proper OTP validation mechanism
  • Validate the OTP against the specified number of digits
  • Ensure that the OTP is associated with the correct user
  • Handle errors and provide appropriate error messages

Compliant code

import play.api.mvc._

object UserController extends Controller {
def register = Action { implicit request =>
val body: AnyContent = request.body
val jsonBody: Option[JsValue] = body.asJson

jsonBody.map { json =>
(json \\ "phoneNumber").asOpt[String].map { phoneNumber =>
(json \\ "otp").asOpt[String].map { otp =>
// Here we are validating the OTP
if (otp.nonEmpty && otp.length == 6) {
// Check if the OTP is associated with the correct user
if (validateOTP(phoneNumber, otp)) {
// Registration process
} else {
BadRequest("Invalid OTP")
}
} else {
BadRequest("Invalid OTP")
}
}.getOrElse {
BadRequest("Missing OTP")
}
}.getOrElse {
BadRequest("Missing phone number")
}
}.getOrElse {
BadRequest("Expecting application/json request body")
}
}

// This is a dummy function, replace it with your actual OTP validation logic
def validateOTP(phoneNumber: String, otp: String): Boolean = {
// Validate the OTP against the user's phone number
true
}
}

The updated code now includes a proper OTP validation mechanism.

Firstly, it checks if the OTP is not empty and if it has the correct length of 6 digits. If the OTP is empty or does not have the correct length, it returns a "BadRequest" with the message "Invalid OTP".

Secondly, it checks if the OTP is associated with the correct user by calling the validateOTP function. This function should contain the logic to validate the OTP against the user's phone number. If the OTP is not valid, it returns a "BadRequest" with the message "Invalid OTP".

This way, the registration process can only proceed if the OTP is not empty, has the correct length, and is associated with the correct user. This prevents the authentication mechanism from being evaded.

Please note that the validateOTP function in this code is a dummy function. You should replace it with your actual OTP validation logic.

References