Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this is a good way to generate a secure random string in JavaScript?

Below is some code I wrote to generate a random string in JavaScript. Are there any security issues if I use the values received from this method as salts or tokens for users?

String.random = function(length, characters) {
    length = length === undefined ? 32 : length;
    characters = characters === undefined ? 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' : characters;

    var maxIndex = characters.length - 1;
    var string = '';

    for(var i = 0; i < length; i++) {
        string += characters[Number.random(0, maxIndex)];
    }

    return string;
}
like image 434
Kirk Ouimet Avatar asked Nov 10 '22 09:11

Kirk Ouimet


1 Answers

You can use crypto.getRandomValues() to get cryptographically secure random numbers in javascript, but they're not decimal numbers like Math.random(), so it takes more work to deal with them. Here's the plain javascipt solution with crypto.getRandomValues():

function getCryptoRandomBetween(min, max){
  //the highest random value that crypto.getRandomValues could store in a Uint32Array
  var MAX_VAL = 4294967295;
  
  //find the number of randoms we'll need to generate in order to give every number between min and max a fair chance
  var numberOfRandomsNeeded = Math.ceil((max - min) / MAX_VAL);
  
  //grab those randoms
  var cryptoRandomNumbers = new Uint32Array(numberOfRandomsNeeded);
  crypto.getRandomValues(cryptoRandomNumbers);
  
  //add them together
  for(var i = 0, sum = 0; i < cryptoRandomNumbers.length; i++){
    sum += cryptoRandomNumbers[i];
  }
  
  //and divide their sum by the max possible value to get a decimal
  var randomDecimal = sum / (MAX_VAL * numberOfRandomsNeeded);
  
  //if result is 1, retry. otherwise, return decimal.
  return randomDecimal === 1 ? getCryptoRandomBetween(min, max) : Math.floor(randomDecimal * (max - min + 1) + min);
}

function getRandomChar(str){
  return str.charAt(getCryptoRandomBetween(0, str.length - 1));
}

String.random = function(length, characters) {
  for(var i = 0, str = ""; i < length; i++) str += getRandomChar(characters);
  return str;
};

console.log(String.random(60, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"));

Pretty rough on the eyes, but it works. If you want a simpler solution, my preference is rando.js. it works for plain javascript as well as Node.js/React. You can use rando(str) to get a random character from a string, so building a random string the way you want is as simple as:

String.random = function(length, characters) {
  for(var i = 0, str = ""; i < length; i++) str += rando(characters);
  return str;
};

console.log(String.random(60, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"));
<script src="https://randojs.com/2.0.0.js"></script>

Let me reiterate that the strings both of these solutions generate have enough entropy to be safe for use as passwords, salts, or tokens for users.

like image 200
Aaron Plocharczyk Avatar answered Nov 15 '22 12:11

Aaron Plocharczyk