Skip to main content

Authentication mechanism absence or evasion - OTP

Need

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

Context

  • Usage of Swift for iOS and macOS app development
  • Usage of UIKit for building modern and responsive user interfaces

Description

Non compliant code

import UIKit

class RegisterViewController: UIViewController {

@IBOutlet weak var phoneNumberTextField: UITextField!
@IBOutlet weak var verificationCodeTextField: UITextField!

@IBAction func registerButtonTapped(_ sender: UIButton) {
let phoneNumber = phoneNumberTextField.text
let verificationCode = verificationCodeTextField.text

// Create new user without verifying the OTP
let newUser = User(phoneNumber: phoneNumber, verificationCode: verificationCode)

// Save the new user
UserDatabase.shared.save(user: newUser)

// Navigate to the next screen
self.performSegue(withIdentifier: "GoToHomeScreen", sender: self)
}
}

In the above code, we have a RegisterViewController where the user inputs their phone number and a verification code that they received via SMS. When the register button is clicked, the registerButtonTapped function is called.

The problem here is that we are not verifying the OTP (One-Time Password) at all. We are just taking the input from the verificationCodeTextField and creating a new user with it. This means that a user can input any value in the verificationCodeTextField and still be able to register. This is a big security flaw as it allows anyone to register with any phone number without needing to verify it.

Even worse, the code does not check if the verification code is 6 digits long, which is the standard length for OTPs. This means that a user can input a code of any length and it will still be accepted. This makes it even easier for an attacker to guess a valid code, as they do not need to guess 6 specific digits.

Steps

  • Implement a proper OTP verification mechanism during the registration process.
  • Generate a random 6-digit verification code and send it to the user's associated cellular number.
  • Validate the entered verification code against the generated code to ensure it matches.
  • Ensure that the verification code has the specified number of digits (6 digits in this case).
  • Reject the registration if the verification code is not valid or does not match the generated code.

Compliant code

import UIKit

class RegisterViewController: UIViewController {

@IBOutlet weak var phoneNumberTextField: UITextField!
@IBOutlet weak var verificationCodeTextField: UITextField!

var generatedVerificationCode: String?

override func viewDidLoad() {
super.viewDidLoad()

// Generate a random 6-digit verification code
generatedVerificationCode = generateVerificationCode()

// Send the verification code to the user's phone number
sendVerificationCode(to: phoneNumberTextField.text, code: generatedVerificationCode)
}

@IBAction func registerButtonTapped(_ sender: UIButton) {
let phoneNumber = phoneNumberTextField.text
let enteredVerificationCode = verificationCodeTextField.text

// Validate the entered verification code
guard let generatedCode = generatedVerificationCode, enteredVerificationCode == generatedCode else {
// Show an error message and return if the verification code is not valid
showError("Invalid verification code. Please try again.")
return
}

// Create new user with the verified phone number
let newUser = User(phoneNumber: phoneNumber, verificationCode: enteredVerificationCode)

// Save the new user
UserDatabase.shared.save(user: newUser)

// Navigate to the next screen
self.performSegue(withIdentifier: "GoToHomeScreen", sender: self)
}

func generateVerificationCode() -> String {
return String(Int.random(in: 100000...999999))
}

func sendVerificationCode(to phoneNumber: String?, code: String?) {
// Implement the function to send the verification code to the user's phone number
}

func showError(_ message: String) {
// Implement the function to show an error message
}
}

The updated code now includes a proper OTP verification mechanism during the registration process.

When the RegisterViewController is loaded, a random 6-digit verification code is generated and sent to the user's associated cellular number.

When the user taps the register button, the entered verification code is validated against the generated code to ensure it matches. If the verification code is not valid or does not match the generated code, an error message is shown and the registration process is halted.

If the verification code is valid and matches the generated code, a new user is created with the verified phone number and saved to the database. The user is then navigated to the next screen.

This way, the vulnerability of the authentication mechanism absence or evasion is fixed.

References