Skip to main content

Insecurely generated token - OTP

Need

Secure generation and transmission of OTP tokens

Context

  • Usage of Dart 2.0 for building high-performance, cross-platform applications
  • Usage of Shelf for managing and organizing data in a Node.js application

Description

Non compliant code

import 'package:shelf/shelf.dart' as shelf;
import 'dart:math';

shelf.Response handleRequest(shelf.Request request) {
var params = request.url.queryParameters;
var phoneNumber = params['phoneNumber'];
var OTP = generateOTP();

// Send OTP to client
return shelf.Response.ok('OTP: $OTP');
}

String generateOTP() {
var rng = new Random();
return List.generate(6, (_) => rng.nextInt(9)).join();
}

In the above code, we have a function handleRequest(shelf.Request request) that handles incoming requests. Inside this function, we extract the phone number from the request query parameters and generate an OTP (One-Time Password).

The OTP is generated using the function generateOTP(), which creates a 6 digit random number. This OTP is then sent back to the client in the response.

The vulnerability lies in the fact that the OTP is generated on the server-side and sent to the client-side. This means that if an attacker intercepts the request, they can easily access the OTP and continue the application flow without needing access to the actual phone number used.

This is a serious security flaw as OTPs are meant to provide a second layer of security by requiring users to have access to a specific device (in this case, a phone). By allowing the OTP to be intercepted, this layer of security is effectively bypassed.

Steps

  • Move the OTP generation to the server-side. This way, the OTP is not exposed to the client-side where it can be intercepted.
  • Ensure the OTP is sent directly to the user's phone number, not returned in the response. This can be done using an SMS gateway service.
  • Implement secure communication between the client and the server. Use HTTPS to encrypt the data in transit and prevent it from being intercepted.
  • Store the OTP in a secure server-side session, associated with the user's session or phone number. This way, when the user submits the OTP, you can compare it with the stored value.
  • Implement a time limit for the OTP. After the limit, the OTP should expire and a new one should be generated for the next request. This prevents an attacker from using an old OTP.
  • Consider adding rate limiting to prevent brute force attacks. This could limit the number of OTP requests from a single IP address or phone number.

Compliant code

import 'package:shelf/shelf.dart' as shelf;
import 'dart:math';
import 'package:shelf_session/shelf_session.dart';
import 'package:sms_maintained/sms.dart';

// Create a session middleware
var sessionMiddleware = SessionMiddleware(
SessionConfig(
sessionKey: 'session',
),
);

// Create a session handler
var handler = const shelf.Pipeline()
.addMiddleware(sessionMiddleware)
.addHandler(_handleRequest);

shelf.Response _handleRequest(shelf.Request request) {
var session = Session.fromRequest(request);
var params = request.url.queryParameters;
var phoneNumber = params['phoneNumber'];

// Generate OTP
var OTP = _generateOTP();

// Store OTP in session
session['OTP'] = OTP;

// Send OTP to user's phone number
_sendOTP(phoneNumber, OTP);

// Do not send OTP in response
return shelf.Response.ok('OTP sent to your phone number');
}

String _generateOTP() {
var rng = new Random();
return List.generate(6, (_) => rng.nextInt(9)).join();
}

void _sendOTP(String phoneNumber, String OTP) {
SmsSender sender = new SmsSender();
SmsMessage message = new SmsMessage(phoneNumber, 'Your OTP is: $OTP');
sender.sendSms(message);
}

The code above fixes the vulnerability by moving the OTP generation to the server-side and sending it directly to the user's phone number using an SMS gateway service.

The OTP is generated using the _generateOTP function, which generates a random 6-digit number. This OTP is then stored in a server-side session using the shelf_session package. The session is associated with the user's request, allowing the OTP to be stored securely and associated with the user's session or phone number.

The OTP is then sent to the user's phone number using the _sendOTP function. This function uses the sms_maintained package to send an SMS to the user's phone number with the OTP.

The OTP is not included in the response sent back to the client, preventing it from being intercepted.

This code also sets up a session middleware and a session handler using the shelf and shelf_session packages. This allows the server to handle multiple sessions and store session data securely.

Please note that this code does not implement a time limit for the OTP, rate limiting, or HTTPS. These features should be implemented as well to further secure the OTP process.

References