Insecurely generated token - OTP - Scala

Insecurely generated token - OTP - Scala

Need

Secure generation and transmission of OTP tokens

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.libs.json for JSON parsing and manipulation in Play Framework

Description

Non compliant code

        import play.api.mvc._
import play.api.libs.json._

class Application extends Controller {

  def generateOTP = Action { request =>
    val json = request.body.asJson.get
    val phoneNumber = (json \\ "phoneNumber").as[String]
    val otp = generateClientSideOTP(phoneNumber)
    Ok(Json.obj("status" -> "success", "OTP" -> otp))
  }

  def generateClientSideOTP(phoneNumber: String): String = {
    // OTP generation logic here
    val otp = phoneNumber.take(4) // This is a simple and insecure OTP generation logic for demonstration purposes
    otp
  }
}
        
        

The above code represents a Scala backend application using the Play framework that generates an OTP client-side and sends it to the server. This approach is insecure because the OTP is generated on the client-side, making it susceptible to interception by an attacker.

The generateOTP function is an action that extracts the phone number from the request body and passes it to the generateClientSideOTP function to generate the OTP. The OTP is then sent back to the client in the response.

The generateClientSideOTP function generates the OTP based on the phone number. In this case, it simply takes the first 4 characters of the phone number as the OTP. This is a very simplistic and insecure method of generating an OTP, and it's used here only for demonstration purposes.

The vulnerability here is that an attacker can intercept the request and gain access to the OTP. Since the OTP is generated on the client-side and sent to the server, the attacker doesn't need access to the phone number used. They only need to intercept the request to access the OTP and continue the application flow.

Steps

  • Move the OTP generation logic to the server-side
  • Generate a cryptographically secure random OTP
  • Use a secure method to transmit the OTP to the client-side
  • Implement proper validation and expiration for the OTP

Compliant code

        import play.api.mvc._
import play.api.libs.json._
import java.security.SecureRandom

class Application extends Controller {

  def generateOTP = Action { request =>
    val json = request.body.asJson.get
    val phoneNumber = (json \\ "phoneNumber").as[String]
    val otp = generateServerSideOTP()
    // Send OTP to the client side securely, e.g., via SMS
    Ok(Json.obj("status" -> "success"))
  }

  def generateServerSideOTP(): String = {
    val random = new SecureRandom()
    val otp = new Array[Byte](4)
    random.nextBytes(otp)
    otp.map("%02x".format(_)).mkString
  }
}
        
        

The OTP generation logic has been moved to the server-side. The generateServerSideOTP function generates a cryptographically secure random OTP using the SecureRandom class. The OTP is then converted to a hexadecimal string.

The OTP is no longer sent in the response to the client. Instead, it should be sent to the client-side securely, for example, via SMS. This is not shown in the code as it depends on the specific SMS gateway used.

The client-side should then send the OTP back to the server for validation. The server should implement proper validation and expiration for the OTP. This is not shown in the code as it depends on the specific requirements and infrastructure of the application.

References