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 Python 3 for scripting and application development
  • Usage of Django for building web applications in Python

Description

Non compliant code

from django.contrib.auth.models import User
from django.http import HttpResponse

def register(request):
username = request.POST['username']
password = request.POST['password']
phone_number = request.POST['phone_number']
otp = request.POST['otp']

if len(otp) != 6:
return HttpResponse('Invalid OTP')

user = User.objects.create_user(username, password)
user.profile.phone_number = phone_number
user.save()

return HttpResponse('User registered successfully')

In the above code, the register function is used to register a new user. The function takes a request as input, which includes a username, password, phone number, and a one-time password (OTP).

The vulnerability lies in the OTP validation. Although the code checks if the OTP length is 6, it does not verify if the OTP matches the one sent to the provided phone number. This allows an attacker to bypass the OTP verification by providing any 6-digit number, thus successfully registering without the need for the actual OTP.

The absence of proper OTP validation makes this code vulnerable to Authentication mechanism absence or evasion - OTP vulnerability. This could potentially allow an unauthorized user to register an account, leading to possible unauthorized access to the application.

Steps

  • Implement a mechanism to generate and send a unique OTP to the user's phone number during the registration process.
  • Store the generated OTP in a secure manner, associating it with the user's session or account creation request.
  • When the user submits the OTP, compare it with the stored OTP for validation.
  • Ensure that the OTP is of the correct length and format before validating.
  • Implement a limit on the number of OTP attempts to prevent brute force attacks.
  • Ensure that the OTP expires after a certain period of time for added security.
  • After successful validation of the OTP, proceed with the account creation process.

Compliant code

from django.contrib.auth.models import User
from django.http import HttpResponse
from django.core.cache import cache
from random import randint

def generate_otp():
return randint(100000, 999999)

def register(request):
username = request.POST['username']
password = request.POST['password']
phone_number = request.POST['phone_number']
otp = request.POST['otp']

if 'otp' not in request.session or request.session['otp'] != otp:
return HttpResponse('Invalid OTP')

if len(otp) != 6:
return HttpResponse('Invalid OTP')

user = User.objects.create_user(username, password)
user.profile.phone_number = phone_number
user.save()

del request.session['otp']

return HttpResponse('User registered successfully')

def send_otp(request):
otp = generate_otp()
request.session['otp'] = otp
phone_number = request.POST['phone_number']
# send otp to phone_number
return HttpResponse('OTP sent successfully')

The updated code now includes a function to generate a unique OTP (One-Time Password) which is a random 6-digit number. This OTP is stored in the user's session data.

During the registration process, the user's submitted OTP is compared with the stored OTP in the session data. If the OTPs do not match or if the OTP is not of the correct length (6 digits), an error message is returned.

After successful validation of the OTP, the user's account is created and the OTP is removed from the session data.

Additionally, a new function send_otp is added to handle the sending of the OTP to the user's phone number. This function generates an OTP, stores it in the session data, and sends it to the user's phone number.

This updated code ensures that the OTP is validated during the registration process, preventing the evasion of the authentication mechanism. It also ensures that the OTP is of the correct length and format, and that it is unique for each registration attempt.

References