Insecurely generated token - Dart

Insecurely generated token - Dart

Need

Secure token generation mechanism

Context

  • Usage of Dart for building cross-platform mobile, web, and desktop applications
  • Usage of dart:math for mathematical calculations and operations
  • Usage of Shelf for building web applications in Dart

Description

Non compliant code

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

String generateToken() {
  var rng = new Random();
  var codeUnits = new List.generate(20, (index){
    return rng.nextInt(33)+89;
  });

  return new String.fromCharCodes(codeUnits);
}

shelf.Response handleRequest(shelf.Request request) {
  var token = generateToken();
  return shelf.Response.ok('Generated Token: $token');
}
        
        

The above code is a Dart backend application that uses the Shelf framework. It includes a function generateToken(), which is used to generate a token that is then sent in the response of handleRequest(shelf.Request request) function.

The generateToken() function uses Dart's Random class to generate a list of 20 random integers, each within the range of 33 to 121 (inclusive). These integers are then converted into their corresponding ASCII characters to form a string, which is returned as the token.

This token generation method is insecure because it does not use a secure cryptographic mechanism. The Random class in Dart is not cryptographically secure, which means the sequence of numbers it generates can be predicted if the initial seed value is known. This makes the token susceptible to cracking, as an attacker could predict the sequence of numbers and therefore the token's hash. Once the hash is cracked, the attacker could obtain the information the token is masking.

Steps

  • Replace the Random class with a secure random number generator. Dart provides a secure RNG in the 'dart:crypto' library.
  • Use the secure RNG to generate a byte array of a suitable length for a token.
  • Convert the byte array to a string representation, such as hexadecimal or base64, to use as the token.

Compliant code

        import 'dart:convert';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:shelf/shelf.dart' as shelf;

String generateToken() {
  var bytes = new Uint8List(20); // Define the length of the token
  var secureRandom = SecureRandom(); // Use a secure random number generator
  for (var i = 0; i < bytes.length; i++) {
    bytes[i] = secureRandom.nextUint8();
  }
  return base64Url.encode(bytes); // Convert the byte array to a base64 string
}

shelf.Response handleRequest(shelf.Request request) {
  var token = generateToken();
  return shelf.Response.ok('Generated Token: $token');
}
        
        

The previous code was using the Random class from the dart:math library to generate a token. This class is not suitable for generating secure tokens because it is not a cryptographically secure random number generator. This means that the tokens it generates can be predicted or reproduced, which is a security vulnerability.

The new code uses the SecureRandom class from the dart:crypto library to generate a token. This class is a cryptographically secure random number generator, which means that the tokens it generates cannot be predicted or reproduced.

The SecureRandom class generates random bytes, so we create a byte array of the desired length for our token. We then fill this array with random bytes.

Finally, we convert the byte array to a string using the base64Url.encode function from the dart:convert library. This function converts the byte array to a base64 string, which is a common format for tokens. This string can be safely sent over the network and stored in databases.

The handleRequest function then uses this secure token generation function to generate a token and send it in the response to the client.

References