148

I want to generate a random string that has to have 5 letters from a-z and 3 numbers.

How can I do this with JavaScript?

I've got the following script, but it doesn't meet my requirements.

        var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        var string_length = 8;
        var randomstring = '';
        for (var i=0; i<string_length; i++) {
            var rnum = Math.floor(Math.random() * chars.length);
            randomstring += chars.substring(rnum,rnum+1);
        }
Shayan Shafiq
  • 1,447
  • 5
  • 18
  • 25
ffffff01
  • 5,068
  • 11
  • 52
  • 61
  • 7
    If it meets your requirement, what's the question then? Also, your forced password requirement is a bad idea. – Rob W Mar 15 '12 at 12:22
  • 9
    http://xkcd.com/936/ – MatuDuke Mar 15 '12 at 12:33
  • 8
    `new Array(12).fill().map(() => String.fromCharCode(Math.random()*86+40)).join("")` Nifty one liner to produce a 12 char password with special chars upper lower numbers in a very lightweight approach – Tofandel Oct 25 '19 at 21:36
  • @RobW Why is it a bad idea? Please explain yourself! – algo Aug 08 '21 at 14:24
  • @algo 8 characters are considered a weak password. An argument could be made that the "standard" length is shifting towards 16. Tho, obviously higher lengths "generally" equal increased security. – Nathan Martin May 31 '22 at 02:55

29 Answers29

391

Forcing a fixed number of characters is a bad idea. It doesn't improve the quality of the password. Worse, it reduces the number of possible passwords, so that hacking by bruteforcing becomes easier.

To generate a random word consisting of alphanumeric characters, use:

var randomstring = Math.random().toString(36).slice(-8);

How does it work?

Math.random()                        // Generate random number, eg: 0.123456
             .toString(36)           // Convert  to base-36 : "0.4fzyo82mvyr"
                          .slice(-8);// Cut off last 8 characters : "yo82mvyr"

Documentation for the Number.prototype.toString and string.prototype.slice methods.

Rob W
  • 341,306
  • 83
  • 791
  • 678
  • 8
    I agree, but sometimes you dont get to decide ;) – ffffff01 Mar 15 '12 at 12:40
  • Could you please explain what's going on in your provided answer? Seems like a nifty trick! – MalSu Nov 23 '12 at 18:20
  • @zessx Which browser are you using? In FF32, I just checked 10.000.000 and got only 18 duplicates. For your numbers it's basically 0. – user562529 Nov 13 '14 at 19:17
  • in Chrome, `.toString(36)` gives a base-36 number with 16 fractional digits, but Math.random() does not give enough possibilities to fill this space, so taking the least significant digits results in a reduced set of available strings. Taking the most significant digits is a better way to do this. [Compare here (use Chrome)](http://jsfiddle.net/dbbfmfdL/) – Luke Dec 04 '14 at 03:58
  • 77
    DO NOT USE THIS: Since the number is converted from binary to decimal, and there is not enough bits to "fill up" the full decimal space, the last digit will only be selected from a certain set of values. For example, on my computer, the last digit is only ever "i", "r", and "9". Use this instead: `Math.random().toString(36).substr(2, 8)` – Joel Jan 19 '16 at 12:59
  • 4
    To explain what 36 does: From [mozilla](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString) _The toString() method parses its first argument, and attempts to return a string representation in the specified radix (base). For radixes above 10, the letters of the alphabet indicate numerals greater than 9. For example, for hexadecimal numbers (base 16), a through f are used._ So according to this, using radix 36, we get a-z after 0-9. – Shishir Gupta Jun 01 '16 at 10:45
  • 1
    @RobW: How to include capital letters as well? – Shishir Gupta Jun 02 '16 at 10:18
  • 2
    @ShishirGupta `.toString` only accepts a base up to and including 36. This answer is meant as a one-liner for a non-cryptographic random string. If you want to have capitals as well, use `Math.random` (or `crypto.getRandomValues` if available) and map the result to a-z, A-Z, 0-9. For instance using saaj's answer below. – Rob W Jun 02 '16 at 10:37
  • 1
    If Math.random() gives `0.125` the result will be `"0.4i"` (invalid password - no 8 chars) – Kamil Kiełczewski Mar 25 '19 at 20:50
  • 3
    Not seeing how this addresses the OP requirement ***"5 letters from a-z and 3 numbers"***. I get it, the author thinks that's a bad idea... but it's the requirement nonetheless – mwag Jun 25 '19 at 16:54
  • 1
    `substr()` is depreciated use `substring()` instead: `Math.random().toString(36).substring(2, 8)` – joecks Jun 02 '23 at 09:00
  • does this avoid the possibility of Math.random() giving you 0? @RobW – mattsmith5 Aug 15 '23 at 05:43
  • @Joel please write your own answer, since you are correct, thanks, your comment is being ignored : ) – mattsmith5 Aug 15 '23 at 05:56
63

A little more maintainable and secure approach.

An update to expand on what I meant and how it works.

  1. Secure. MDN is pretty explicit about the use of Math.random for anything related to security:

    Math.random() does not provide cryptographically secure random numbers. Do not use them for anything related to security. Use the Web Crypto API instead, and more precisely the window.crypto.getRandomValues() method.

    Looking at the can-i-use for getRandomValues in 2020 you probably don't need the msCrypto and Math.random fallback any more, unless you care about ancient browsers.

  2. Maintainable is mostly about the RegExp _pattern as an easy way to define what character classes you allow in the password. But also about the 3 things where each does its job: defines a pattern, gets a random byte as securely as possible, provides a public API to combine the two.

var Password = {
 
  _pattern : /[a-zA-Z0-9_\-\+\.]/,
  
  
  _getRandomByte : function()
  {
    // http://caniuse.com/#feat=getrandomvalues
    if(window.crypto && window.crypto.getRandomValues) 
    {
      var result = new Uint8Array(1);
      window.crypto.getRandomValues(result);
      return result[0];
    }
    else if(window.msCrypto && window.msCrypto.getRandomValues) 
    {
      var result = new Uint8Array(1);
      window.msCrypto.getRandomValues(result);
      return result[0];
    }
    else
    {
      return Math.floor(Math.random() * 256);
    }
  },
  
  generate : function(length)
  {
    return Array.apply(null, {'length': length})
      .map(function()
      {
        var result;
        while(true) 
        {
          result = String.fromCharCode(this._getRandomByte());
          if(this._pattern.test(result))
          {
            return result;
          }
        }        
      }, this)
      .join('');  
  }    
    
};
<input type='text' id='p'/><br/>
<input type='button' value ='generate' onclick='document.getElementById("p").value = Password.generate(16)'>
saaj
  • 23,253
  • 3
  • 104
  • 105
  • 1
    Furthermore this doesn't always add a number to the generated code and the OP wants minimum 3. – qwertzman Oct 18 '16 at 18:15
  • Found a library that support window.crypto.getRandomValues and works in node.js as well https://github.com/bermi/password-generator – Eugene Jan 26 '18 at 13:44
  • Just wanted to say I needed something to generate passwords in a pinch and this was perfect. Thank you! – butterednapkins Apr 17 '19 at 19:49
  • seems everyone who answered just made a random password gen lol. yours is pretty close if tweaked abit could actually more match the question, change `_pattern` to `_charset` and actually, add a `_pattern` match to determine the condition https://playcode.io/774196/ – Lawrence Cherone May 28 '21 at 17:21
53

For someone who is looking for a simplest script. No while (true), no if/else, no declaration.

Base on mwag's answer, but this one uses crypto.getRandomValues, a stronger random than Math.random.

var generatePassword = (
  length = 20,
  wishlist = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~!@-#$'
) =>
  Array.from(crypto.getRandomValues(new Uint32Array(length)))
    .map((x) => wishlist[x % wishlist.length])
    .join('')

console.log(generatePassword())

Bookmarklet

javascript:prompt("Random password:",((o=20,n="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~!@-#$")=>Array.from(crypto.getRandomValues(new Uint32Array(o))).map(o=>n[o%n.length]).join(""))())

Node.js

const crypto = require('crypto')

const generatePassword = (
  length = 20,
  wishlist = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~!@-#$'
) =>
  Array.from(crypto.randomFillSync(new Uint32Array(length)))
    .map((x) => wishlist[x % wishlist.length])
    .join('')

console.log(generatePassword())

Python

#!/usr/bin/env python

import os


def rand(length: int) -> str:
    bytes = os.urandom(length)
    chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-"
    cl = len(chars)
    rs = ''
    for i in bytes:
        rs += chars[i % cl]
    return rs


print(rand(18))

Go

See: https://stackoverflow.com/a/67035900/1099314

ninhjs.dev
  • 7,203
  • 1
  • 49
  • 35
  • 1
    This is not giving consistently 20 length passwords, because of an undefined offset. The `+ 1` part is not necessary and can be removed. – bryc Jul 07 '19 at 10:45
  • Good catch @bryc. Updated. Thanks. – ninhjs.dev Jul 07 '19 at 15:15
  • 1
    Useful and easy solution. Anyone looking to do this within NodeJS can simply replace `crypto.getRandomValues` with [crypto.randomFillSync](https://nodejs.org/docs/latest/api/crypto.html#crypto_crypto_randomfillsync_buffer_offset_size). – Rvy Pandey May 08 '20 at 15:26
  • 1
    Added Node.js version. Thanks @Rvy Pandey. – ninhjs.dev May 09 '20 at 02:02
  • 5
    This create uniformity issue. Mentioned in nanoid doc https://github.com/ai/nanoid, random % alphabet is a popular mistake to make when coding an ID generator. The distribution will not be even; there will be a lower chance for some symbols to appear compared to others. – Gnought Jul 10 '22 at 15:22
  • 2
    @ Gnuogh this is a simple solution for generating password. If developers want more secure, they should use the tested packages. There are a lot of packages out there, not just nano as you tried advertising. nano also does random likes that but does some steps to random characters, not impressed. It could be use for generating id, not password. – ninhjs.dev Aug 30 '22 at 15:58
  • For the python version, a simpler solution is using pythons built in secrets module; `secrets.token_urlsafe(length)` https://stackoverflow.com/a/61471228/12757511 – Michael Bauer May 03 '23 at 23:44
49

Many answers (including the original of this one) don't actually answer the letter- and number-count requirements of the OP.

Below are two solutions: general (no min letters/numbers), and with rules as specified in the OP.


UPDATE 12/2022 re "security"

Many other answers berate the use of Math.random() and then go on to use a better random number generator that is non-uniformly applied, with an end result that (just like using Math.random!) is not cryptographically secure. Anyway, fwiw this update addresses both, which I don't believe any other answer does.


General:

Advantages of this approach:

  • it's more secure than accepted/highest-voted answer, and also more versatile, because first, it supports any case-sensitive character (including any of the ~150k (at the time of this writing) unicode chars, rather than just one of 36 case-insensitive alphanumeric ones), and second, it uses a secure random generator that is applied uniformly
  • it's more concise than other answers (for general solution, 3 lines max; can be one-liner) (OK well, sort of depending on how much you squint your eyes looking at a "line")
  • it uses only native Javascript- no installation or other libs required

Note that this:

  • assumes that crypto.getRandomValues and Array.fill() are both available

Three-liner:

var pwdChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var pwdLen = 10;
var randPassword = new Array(pwdLen).fill(0).map(x => (function(chars) { let umax = Math.pow(2, 32), r = new Uint32Array(1), max = umax - (umax % chars.length); do { crypto.getRandomValues(r); } while(r[0] > max); return chars[r[0] % chars.length]; })(pwdChars)).join('');

Or, as one-liner:

var randPassword = new Array(10).fill("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz").map(x => (function(chars) { let umax = Math.pow(2, 32), r = new Uint32Array(1), max = umax - (umax % chars.length); do { crypto.getRandomValues(r); } while(r[0] > max); return chars[r[0] % chars.length]; })(x)).join('');

With Letter / Number Rules

Now, a variation on the above. This will generate three random strings from the given charsets (letter, number, either) and then scramble the result.

First, as a function:

function randPassword(letters, numbers, either) {
  var chars = [
   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", // letters
   "0123456789", // numbers
   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" // either
  ];

  function randInt(this_max) { // return int between 0 and this_max - 1
    let umax = Math.pow(2, 32);
    let max = umax - (umax % this_max);
    let r = new Uint32Array(1);
    do {
      crypto.getRandomValues(r);
    } while(r[0] > max);
    return r[0] % this_max;
  }

  function randCharFrom(chars) {
    return chars[randInt(chars.length)];
  }

  function shuffle(arr) { // https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
    for (let i = 0, n = arr.length; i < n - 2; i++) {
        let j = randInt(n - i);
        [arr[j], arr[i]] = [arr[i], arr[j]];
    }
    return arr;
  }

  return shuffle([letters, numbers, either].map(function(len, i) {
    return Array(len).fill(chars[i]).map(x => randCharFrom(x)).join('');
  }).concat().join('').split('')).join('')
}

// invoke like so: randPassword(5,3,2);

Same thing, as a 3-liner (well, more like a lot of lines smashed into 3 lines. Not recommended but sometimes it's entertaining anyway):

var chars = ["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz","0123456789", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"];
function randInt(this_max) { let umax = Math.pow(2, 32); let max = umax - (umax % this_max); let r = new Uint32Array(1); do { crypto.getRandomValues(r); } while(r[0] > max); return r[0] % this_max; };

var randPassword = [[5,3,2].map(function(len, i) { return new Array(len).fill(chars[i]).map(x => (function(chars) { let umax = Math.pow(2, 32), r = new Uint32Array(1), max = umax - (umax % chars.length); do { crypto.getRandomValues(r); } while(r[0] > max); return chars[r[0] % chars.length]; })(x)).join(''); }).join('')].map(s => { let arr = s.split(''); for (let i = 0, n = arr.length; i < n - 2; i++) { let j = randInt(n - i); [arr[j], arr[i]] = [arr[i], arr[j]]; } return arr.join(''); })[0];
mwag
  • 3,557
  • 31
  • 38
  • Good answer, but the capital letter Y will never appear in any password you generate. – Ryan Shillington Mar 31 '17 at 02:52
  • 1
    Thanks. I used the charset in the OP question but looks like it was flawed. Updated now. – mwag Apr 01 '17 at 04:08
  • 1
    You say "it's more secure than accepted/highest-voted answer". Could you please elaborate on the reason? – BenjaminH Apr 29 '17 at 15:03
  • 2
    Also the use of Math.random() can be predictable. So for generating passwords better use a function like window.crypto.getRandomValues. – BenjaminH Apr 29 '17 at 15:04
  • 2
    The reason it is more secure is because it supports is a higher number of possible characters in the output than using toString(36). With a fixed output length (e.g. of 8), if you have a max of 36 chars to choose from, you have far fewer permutations than if you have 62 chars (as in the example) or better yet, the whole 95 printable (ascii) chars, or if using unicode, an even higher number of possibilities per byte. – mwag May 01 '17 at 16:52
  • I agree window.crypto.getRandomValues is better. That improvement was not intended to be the focus of the answer, but no harm including a note that, where available, better to use it. It's been updated. Thanks – mwag May 01 '17 at 16:53
  • 1
    I [rewrote this to be more verbose](https://gist.github.com/iandunn/79acc38755033b2e94b81fb3ad3dfc4b) (and IMO readable) if that interests anyone. AFAICT, the results are the same, it's just easier to grok/maintain. LMK if anyone sees any security issues, though. – Ian Dunn Dec 30 '22 at 21:35
13

In case you need a password generated with at least 1 number, 1 upper case character, and 1 lower case character:

function generatePassword(passwordLength) {
  var numberChars = "0123456789";
  var upperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  var lowerChars = "abcdefghijklmnopqrstuvwxyz";
  var allChars = numberChars + upperChars + lowerChars;
  var randPasswordArray = Array(passwordLength);
  randPasswordArray[0] = numberChars;
  randPasswordArray[1] = upperChars;
  randPasswordArray[2] = lowerChars;
  randPasswordArray = randPasswordArray.fill(allChars, 3);
  return shuffleArray(randPasswordArray.map(function(x) { return x[Math.floor(Math.random() * x.length)] })).join('');
}

function shuffleArray(array) {
  for (var i = array.length - 1; i > 0; i--) {
    var j = Math.floor(Math.random() * (i + 1));
    var temp = array[i];
    array[i] = array[j];
    array[j] = temp;
  }
  return array;
}

alert(generatePassword(12));

Here's the fiddle if you want to play/test: http://jsfiddle.net/sJGW4/155/

Props to @mwag for giving me the start to create this.

Ryan Shillington
  • 23,006
  • 14
  • 93
  • 108
  • 2
    You're missing lowercase `j` :P Otherwise, good stuff right here. Modified it a bit to meet my requirements though. – Igor Yavych Aug 17 '17 at 22:11
  • In case anyone looks at this answer without reading others, it's important to know that `Math.random` is not cryptographically secure. If you're going to use this answer, you should change it to `crypto.getRandomValues()`. – Ian Dunn Dec 30 '22 at 17:04
  • I like this version, but I added var specialchars = "!@#$%^&*()_+.... – Robert Koernke May 11 '23 at 16:27
11

As @RobW notes, restricting the password to a fixed number of characters as proposed in the OP scheme is a bad idea. But worse, answers that propose code based on Math.random are, well, a really bad idea.

Let's start with the bad idea. The OP code is randomly selecting a string of 8 characters from a set of 62. Restricting the random string to 5 letters and 3 numbers means the resulting passwords will have, at best, 28.5 bits of entropy (as opposed to a potential of 47.6 bits if the distribution restriction of 5 letters and 3 numbers were removed). That's not very good. But in reality, the situation is even worse. The at best aspect of the code is destroyed by the use of Math.random as the means of generating entropy for the passwords. Math.random is a pseudo random number generator. Due to the deterministic nature of pseudo random number generators the entropy of the resulting passwords is really bad , rendering any such proposed solution a really bad idea. Assuming these passwords are being doled out to end users (o/w what's the point), an active adversary that receives such a password has very good chance of predicting future passwords doled out to other users, and that's probably not a good thing.

But back to the just bad idea. Assume a cryptographically strong pseudo random number generator is used instead of Math.random. Why would you restrict the passwords to 28.5 bits? As noted, that's not very good. Presumably the 5 letters, 3 numbers scheme is to assist users in managing randomly doled out passwords. But let's face it, you have to balance ease of use against value of use, and 28.5 bits of entropy isn't much value in defense against an active adversary.

But enough of the bad. Let's propose a path forward. I'll use the JavaScript EntropyString library which "efficiently generates cryptographically strong random strings of specified entropy from various character sets". Rather than the OP 62 characters, I'll use a character set with 32 characters chosen to reduce the use of easily confused characters or the formation of English words. And rather than the 5 letter, 3 number scheme (which has too little entropy), I'll proclaim the password will have 60 bits of entropy (this is the balance of ease versus value).

import { Entropy, charSet32 } from 'entropy-string'
const random = new Entropy({ bits: 60, charset: charset32 })
const string = random.string()

"Q7LfR8Jn7RDp"

Note the arguments to Entropy specify the desired bits of entropy as opposed to more commonly seen solutions to random string generation that specify passing in a string length (which is both misguided and typically underspecified, but that's another story).

dingo sky
  • 1,445
  • 17
  • 15
8

Any password generated with Math.random() is EXTREMELY BAD.

This function uses the system time as a seed for the random number generator. Anyone who knows the time the password was generated can easily brute-force the password.

In almost all cases, this data is easily available - just take the registration_time column in a hacked database, and test out all the values generated by the Math.random() algorithm using the times from 15 to 0 minutes before.

A password generated with Math.random() is completely worthless because the time the password was first used is enough for cracking it.

7

This isn't exactly optimized, but it should work.

var chars = "ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
var string_length = 8;
var randomstring = '';
var charCount = 0;
var numCount = 0;

for (var i=0; i<string_length; i++) {
    // If random bit is 0, there are less than 3 digits already saved, and there are not already 5 characters saved, generate a numeric value. 
    if((Math.floor(Math.random() * 2) == 0) && numCount < 3 || charCount >= 5) {
        var rnum = Math.floor(Math.random() * 10);
        randomstring += rnum;
        numCount += 1;
    } else {
        // If any of the above criteria fail, go ahead and generate an alpha character from the chars string
        var rnum = Math.floor(Math.random() * chars.length);
        randomstring += chars.substring(rnum,rnum+1);
        charCount += 1;
    }
}

alert(randomstring);

​ ​ ​

Here's a jsfiddle for you to test on: http://jsfiddle.net/sJGW4/3/

Alec Sanger
  • 4,442
  • 1
  • 33
  • 53
5

I have written a small one inspired from your answer:

(function(){g=function(){c='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';p='';for(i=0;i<8;i++){p+=c.charAt(Math.floor(Math.random()*62));}return p;};p=g();while(!/[A-Z]/.test(p)||!/[0-9]/.test(p)||!/[a-z]/.test(p)){p=g();}return p;})()

This function returns the password and can be used in bookmarklet like:

javascript:alert(TheCodeOfTheFunction);
skink
  • 5,133
  • 6
  • 37
  • 58
5

My Crypto based take on the problem. Using ES6 and omitting any browser feature checks. Any comments on security or performance?

const generatePassword = (
  passwordLength = 12,
  passwordChars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
) =>
  [...window.crypto.getRandomValues(new Uint32Array(passwordLength))]
    .map(x => passwordChars[x % passwordChars.length])
    .join('');
Rubinous
  • 464
  • 6
  • 12
  • This works great. It's more readable than most of the other functions, which I prefer. – Gavin Apr 22 '21 at 09:20
  • 1
    Using `%` [introduces a bias in the distribution](https://dimitri.xyz/random-ints-from-random-bits/) – Ian Dunn Jan 18 '23 at 17:23
3

Ok so if I understand well you're trying to get a random string password which contains 5 letters and 3 numbers randomly positioned and so which has a length of 8 characters and you accept maj and min letters, you can do that with the following function:

function randPass(lettersLength,numbersLength) {
    var j, x, i;
    var result           = '';
    var letters       = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    var numbers       = '0123456789';
    for (i = 0; i < lettersLength; i++ ) {
        result += letters.charAt(Math.floor(Math.random() * letters.length));
    }
    for (i = 0; i < numbersLength; i++ ) {
        result += numbers.charAt(Math.floor(Math.random() * numbers.length));
    }
    result = result.split("");
    for (i = result.length - 1; i > 0; i--) {
        j = Math.floor(Math.random() * (i + 1));
        x = result[i];
        result[i] = result[j];
        result[j] = x;
    }
    result = result.join("");
    return result
}

function randPass(lettersLength,numbersLength) {
    var j, x, i;
    var result           = '';
    var letters       = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    var numbers       = '0123456789';
    for (i = 0; i < lettersLength; i++ ) {
        result += letters.charAt(Math.floor(Math.random() * letters.length));
    }
    for (i = 0; i < numbersLength; i++ ) {
        result += numbers.charAt(Math.floor(Math.random() * numbers.length));
    }
    result = result.split("");
    for (i = result.length - 1; i > 0; i--) {
        j = Math.floor(Math.random() * (i + 1));
        x = result[i];
        result[i] = result[j];
        result[j] = x;
    }
    result = result.join("");
    return result
}
console.log(randPass(5,3))
Woold
  • 783
  • 12
  • 23
2

I wouldn't recommend using a forced password as it restricts the User's Security but any way, there are a few ways of doing it -

Traditional JavaScript Method -

Math.random().toString(36).slice(-8);

Using Random String

Install random string:

npm install randomstring

Using it in App.js -

var randStr = require('randomstring');

var yourString = randStr.generate(8);

The Value of your password is being hold in the variable yourString.

Don't Use A Forced Password!

Forced Password can harm your security as all the passwords would be under the same character set, which might easily be breached!

Daksh M.
  • 4,589
  • 4
  • 30
  • 46
  • Force != Enforce.. letting a user to enter 123456?.. In a secure system its actually needed to generate a password based upon a condition required by validation.. Without a conditional generation, the code could create invalid passwords, i.e >= 8 characters, at least 1 upper & lower case and a number or special character. Making sure users have strong passwords is not *restricting the user's security* – Lawrence Cherone May 28 '21 at 17:31
  • `Math.random` is not cryptographically secure, please read the other answers – Ian Dunn Dec 30 '22 at 17:10
2

Here's a way to create a flexible generator that allows you to add some rules:

function generatePassword(length, rules) {
    if (!length || length == undefined) {
        length = 8;
    }

    if (!rules || rules == undefined) {
        rules = [
            {chars: "abcdefghijklmnopqrstuvwxyz", min: 3},  // As least 3 lowercase letters
            {chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", min: 2},  // At least 2 uppercase letters
            {chars: "0123456789", min: 2},                  // At least 2 digits
            {chars: "!@#$&*?|%+-_./:;=()[]{}", min: 1}      // At least 1 special char
        ];
    }

    var allChars = "", allMin = 0;
    rules.forEach(function(rule) {
        allChars += rule.chars;
        allMin += rule.min;
    });
    if (length < allMin) {
        length = allMin;
    }
    rules.push({chars: allChars, min: length - allMin});
    
    var pswd = "";
    rules.forEach(function(rule) {
        if (rule.min > 0) {
            pswd += shuffleString(rule.chars, rule.min);
        }
    });
    
    return shuffleString(pswd);
}

function shuffleString(str, maxlength) {
    var shuffledString = str.split('').sort(function(){return 0.5-Math.random()}).join('');
    if (maxlength > 0) {
        shuffledString = shuffledString.substr(0, maxlength);
    }
    return shuffledString;
}

var pswd = generatePassword(15, [
  {chars: "abcdefghijklmnopqrstuvwxyz", min: 4},  // As least 4 lowercase letters
  {chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", min: 1},  // At least 1 uppercase letters
  {chars: "0123456789", min: 3},                  // At least 3 digits
  {chars: "!@#$&*?|%+-_./:;=()[]{}", min: 2}      // At least 2 special chars
]);

console.log(pswd, pswd.length);
Jaime Neto
  • 29
  • 3
2

Based on @Ryan Shillington answer above you may find this enhancment helpfull too. Think this is more secured then what was requeted on the original request in the question above.

  1. Password generated with at least 1 number, 1 upper case character, 1 lower case character and 1 Special character
  2. Password length is dynamic

//Password generated with at least 1 number, 1 upper case character, 1 lower case character and 1 Special character
function generatePassword() 
{
      var passwordLength = randomIntFromInterval(10,20);    
      var numberChars = "0123456789";
      var upperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
      var lowerChars = "abcdefghijklmnopqrstuvwxyz";
      var specialChars = "~!#$%&*-+|";
      var allChars = numberChars + upperChars + lowerChars + specialChars;
      var randPasswordArray = Array(passwordLength);
      randPasswordArray[0] = numberChars;
      randPasswordArray[1] = upperChars;
      randPasswordArray[2] = lowerChars;
      randPasswordArray[3] = specialChars;
      randPasswordArray = randPasswordArray.fill(allChars, 4);
      if(window.crypto && window.crypto.getRandomValues)
      {
          return shuffleArray(randPasswordArray.map(function(x) { return x[Math.floor(window.crypto.getRandomValues(new Uint32Array(1))[0] / (0xffffffff + 1) * x.length)] })).join('');  
      }
      else if(window.msCrypto && window.msCrypto.getRandomValues) 
      {
          return shuffleArray(randPasswordArray.map(function(x) { return x[Math.floor(window.msCrypto.getRandomValues(new Uint32Array(1))[0] / (0xffffffff + 1) * x.length)] })).join('');
      }else{
          return shuffleArray(randPasswordArray.map(function(x) { return x[Math.floor(Math.random() * x.length)] })).join('');
      }
      
    }

    function shuffleArray(array) 
    {
      for (var i = array.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * (i + 1));
        var temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
     
     return array;
}

//generate random number in the range (min and max included)
function randomIntFromInterval(min, max) {
      return Math.floor(Math.random() * (max - min + 1) + min);
}
<input type='text' id='p9'/>
<input type='button' value ='pass generator' onclick='document.getElementById("p9").value = generatePassword()'>
Danny L.
  • 93
  • 1
  • 1
  • 8
1

I'm loathe to add a 29th answer here, but I looked at all of the others closely and none seemed secure enough for my requirements.

mwag's answer is close, but I couldn't easily verify that it actually avoids bias in the distribution. It still uses a modulus operation, and the max/umax technique isn't documented or obvious. I also feel like the code has low readability, even after reformatting.

This code uses the rejection sampling technique, which feels simple and straight-forward to me.

/**
 * Generate a cryptographically secure random password.
 *
 * @returns {string}
 */
function generatePassword() {
    const characterPool  = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()';
    const passwordLength = 24;
    const randomNumber   = new Uint8Array( 1 );
    let password         = '';

    // JS doesn't provide a way to generate a cryptographically secure random number within a range, so instead
    // we just throw out values that don't correspond to a character. This is a little bit slower than using a
    // modulo operation, but it avoids introducing bias in the distribution. Realistically, it's easily performant
    // in this context.
    // @link https://dimitri.xyz/random-ints-from-random-bits/
    for ( let i = 0; i < passwordLength; i++ ) {
        do {
            crypto.getRandomValues( randomNumber );
        } while ( randomNumber[0] >= characterPool.length );

        password += characterPool[ randomNumber[0] ];
    }

    return password;
}

My company has released it as a package with configurable arguments for length and characters. bermi/password-generator works in a similar way, but isn't as lean.


Aside: I also looked at existing solutions for passphrases, but unfortunately didn't find a secure one that worked for me. The two that seemed best were:

  • niceware - This looks secure and trustworthy, but its dictionary includes vulgar/racist words, so it's not suitable for most applications IMO.
  • eff-diceware-passphrase - This also looks trustworthy, and has the benefit of using the EFF large wordlist, which doesn't include vulgar/racist/etc words. It only works in Node environments, though.
Ian Dunn
  • 3,541
  • 6
  • 26
  • 44
1
var letters = ['a','b','c','d','e','f','g','h','i','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
    var numbers = [0,1,2,3,4,5,6,7,8,9];
    var randomstring = '';

        for(var i=0;i<5;i++){
            var rlet = Math.floor(Math.random()*letters.length);
            randomstring += letters[rlet];
        }
        for(var i=0;i<3;i++){
            var rnum = Math.floor(Math.random()*numbers.length);
            randomstring += numbers[rnum];
        }
     alert(randomstring);
Noki
  • 451
  • 3
  • 6
0

And finally, without using floating point hacks:

function genpasswd(n) {
    // 36 ** 11 > Number.MAX_SAFE_INTEGER
    if (n > 10)
        throw new Error('Too big n for this function');
    var x = "0000000000" + Math.floor(Number.MAX_SAFE_INTEGER * Math.random()).toString(36);
    return x.slice(-n);
}
socketpair
  • 1,893
  • 17
  • 15
0

Secure password with one upperCase char.

let once = false;

    let newPassword = Math.random().toString(36).substr(2, 8).split('').map((char) => {
                    if(!Number(char) && !once){
                        once = true;
                        return char.toUpperCase();
                    }
                    return char;
                }).join('');

    console.log(newPassword)
Dusty
  • 354
  • 4
  • 15
0

Try this, it works.

enishant/random_password.js

Download script to your javascript application and call the function, randomPassword()

Shayan Shafiq
  • 1,447
  • 5
  • 18
  • 25
Nishant
  • 358
  • 2
  • 5
  • 11
  • What's the benefit of using a 3rd party library when browsers have a native CSPRING like `crypto.getRandomValues()` ? It's also not secure because it uses `Math.random` – Ian Dunn Dec 30 '22 at 17:12
0

Well, you can always use window.crypto object available in the recent version of browser.

Just need one line of code to get a random number:

let n = window.crypto.getRandomValues(new Uint32Array(1))[0];

It also helps to encrypt and decrypt data. More information at MDN Web docs - window.crypto.

Caiuby Freitas
  • 279
  • 2
  • 7
  • This doesn't seem to add anything to what the other answers have said about `crypto.getRandomNumbers`, and doesn't offer a complete solution to meet OP's requirements. Having 28 answers to a question makes it harder to find the good ones. – Ian Dunn Dec 30 '22 at 17:08
0

Create a Password generator service called PassswordGeneratorService

import { Injectable } from '@angular/core';

@Injectable()
export class PasswordGeneratorService {

  generatePassword(length:number,upper:boolean,numbers:boolean,symbols:boolean) {
    const passwordLength = length || 12;
    const addUpper =  upper;
    const addNumbers =  numbers;
    const addSymbols =  symbols;

    const lowerCharacters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
    const upperCharacters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
    const numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
    const symbols = ['!', '?', '@'];

    const getRandom = array => array[Math.floor(Math.random() * array.length)];

    let finalCharacters = '';

    if (addUpper) {
      finalCharacters = finalCharacters.concat(getRandom(upperCharacters));
    }

    if (addNumbers) {
      finalCharacters = finalCharacters.concat(getRandom(numbers));
    }

    if (addSymbols) {
      finalCharacters = finalCharacters.concat(getRandom(symbols));
    }

    for (let i = 1; i < passwordLength - 3; i++) {
      finalCharacters = finalCharacters.concat(getRandom(lowerCharacters));
    }

    return  finalCharacters.split('').sort(() => 0.5 - Math.random()).join('');
  }
}

don't forget to add the service on the module your using

@NgModule({
  imports: [
    CommonModule,
    SharedModule,
    CommonModule,
    RouterModule.forChild(routes),
    FormsModule,
    ReactiveFormsModule,
    FlexLayoutModule,
    TranslateModule,
    ExistingUserDialogModule,
    UserDocumentsUploadDialogModule
  ],
  declarations: [
    UserListComponent,
    EditUserDialogComponent,
    UserEditorComponent
  ],
  entryComponents: [
    EditUserDialogComponent
  ],
  providers: [
    AuthService,
    PasswordGeneratorService
  ]
})
export class UsersModule {
}

On you controller add a method which calls the generate password method inside the service and set the result on the password field

  constructor(
     private passwordGenerator: PasswordGeneratorService,
    )
  get newPassword() {
    return this.password.get('newPassword');
  }
  generatePassword() {
    this.newPassword.setValue(this.passwordGenerator.generatePassword(8,true,true,true));
  }
Eyayu Tefera
  • 771
  • 9
  • 9
0

Generate a random password of length 8 to 32 characters with at least 1 lower case, 1 upper case, 1 number, 1 spl char (!@$&)

    function getRandomUpperCase() {
       return String.fromCharCode( Math.floor( Math.random() * 26 ) + 65 );
    }
    
    function getRandomLowerCase() {
       return String.fromCharCode( Math.floor( Math.random() * 26 ) + 97 );
    } 
    
    function getRandomNumber() {
       return String.fromCharCode( Math.floor( Math.random() * 10 ) + 48 );
    }
    
    function getRandomSymbol() {
        // const symbol = '!@#$%^&*(){}[]=<>/,.|~?';
        const symbol = '!@$&';
        return symbol[ Math.floor( Math.random() * symbol.length ) ];
    }
    
    const randomFunc = [ getRandomUpperCase, getRandomLowerCase, getRandomNumber, getRandomSymbol ];
    
    function getRandomFunc() {
        return randomFunc[Math.floor( Math.random() * Object.keys(randomFunc).length)];
    }
    
    function generatePassword() {
        let password = '';
        const passwordLength = Math.random() * (32 - 8) + 8;
        for( let i = 1; i <= passwordLength; i++ ) {
            password += getRandomFunc()();
        }
        //check with regex
        const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,32}$/
        if( !password.match(regex) ) {
            password = generatePassword();
        }
        return password;
    }
    
    console.log( generatePassword() );
-1

var Password = {
 
  _pattern : /[a-zA-Z0-9_\-\+\.]/,
  
  
  _getRandomByte : function()
  {
    // http://caniuse.com/#feat=getrandomvalues
    if(window.crypto && window.crypto.getRandomValues) 
    {
      var result = new Uint8Array(1);
      window.crypto.getRandomValues(result);
      return result[0];
    }
    else if(window.msCrypto && window.msCrypto.getRandomValues) 
    {
      var result = new Uint8Array(1);
      window.msCrypto.getRandomValues(result);
      return result[0];
    }
    else
    {
      return Math.floor(Math.random() * 256);
    }
  },
  
  generate : function(length)
  {
    return Array.apply(null, {'length': length})
      .map(function()
      {
        var result;
        while(true) 
        {
          result = String.fromCharCode(this._getRandomByte());
          if(this._pattern.test(result))
          {
            return result;
          }
        }        
      }, this)
      .join('');  
  }    
    
};
<input type='text' id='p'/><br/>
<input type='button' value ='generate' onclick='document.getElementById("p").value = Password.generate(16)'>
-1

You could use lodash.

import * as _ from 'lodash';

export const generateCustomPassword = (
  lowerCaseCount: number,
  upperCaseCount: number,
  numbersCount: number,
  specialsCount: number,
) => {
  const chars = 'abcdefghijklmnopqrstuvwxyz';
  const numberChars = '0123456789';
  const specialChars = '!"£$%^&*()-=+_?';
  const pickedChars = _.sampleSize(chars, lowerCaseCount)
    .concat(_.sampleSize(chars.toUpperCase(), upperCaseCount))
    .concat(_.sampleSize(numberChars, numbersCount))
    .concat(_.sampleSize(specialChars, specialsCount));
  return _.shuffle(pickedChars).join('');
};


generateCustomPassword(4, 4, 4, 1)
het
  • 781
  • 9
  • 16
  • Those don't appear to use a cryptographically-secure random number generator, so they have the same problem as `Math.random` – Ian Dunn Dec 30 '22 at 17:15
-1

Use this npm package for generating secure password :

npm i generate-secure-password

https://www.npmjs.com/package/generate-secure-password

Sample Code :

const generatePassword = require('generate-secure-password');
let yourPassword = generatePassword({
    // None of the arguments are mandatory
    length: 8,//not less than 4
    numbers: false,//false if number needs to be excluded from password, else true
    symbols: false,//false if symbols needs to be excluded from password, else true
    lowercase: false,//false if lowercase character  needs to be excluded from password, else true
    uppercase: false,//false if uppercase character needs to be excluded from password, else true
    exclude: ['"','%'] //symbols to exclude, Pass it as an array
});
console.log(yourPassword) 
lekshmi
  • 338
  • 3
  • 9
  • Can't find the repo from the NPM page. It's either amateurish or shady, either cases are a red flags, especially for a password generator. – Robin Goupil Mar 24 '22 at 08:59
  • The code can be [seen on NPM](https://www.npmjs.com/package/generate-secure-password?activeTab=explore). It uses `Math.random` so it has the same problem as other insecure answers. – Ian Dunn Dec 30 '22 at 17:18
-1

Everyone here is using Math.random(), which is cryptographically insecure

Use this instead using the crypto API.

const crypto = require("crypto");

function generatePassword() {
    return Array(12)
        .fill("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
        .map(function (x) {
            return x[crypto.randomInt(0, 10_000) % x.length];
        })
        .join("");
}

Edit: if you’re using crypto-browserfy you need to use the randomBytes method.

const crypto = require("crypto");

function generatePassword() {
    return Array(12)
        .fill("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
        .map(function (x) {
            let int = crypto.randomBytes(4, function(ex, buf) {
                var hex = buf.toString('hex');
                var myInt32 = parseInt(hex, 16);
            });
            return x[int % x.length];
        })
        .join("");
}
thecoolwinter
  • 861
  • 7
  • 22
  • I get the following error when adding to my gatsby project: Module not found: Error: Can't resolve 'crypto' in [file]. I tried running npm i crypto-js but no dice. is there a configuration you left out? – Edward Jul 08 '22 at 17:11
  • @Edward the crypto library is a lib packaged in nodejs. Gatsby may not have the same library. Google "Gatsby secure random number generator" and replace the `crypto.randomInt` with Gatsby's solution. – thecoolwinter Jul 08 '22 at 17:13
  • I'm gonna need a link there boss. Google doesnt return anything relevant. I've tried all the remedies for the "crypto not found" error and still nothing. I might just need to use math random. – Edward Jul 08 '22 at 17:40
  • I was able to make it past the can't resolve using [this solution](https://stackoverflow.com/questions/67333737/add-crypto-browserify-to-gatsby-project) but now I get `crypto.randomInt` is not a function so I'm just assuming this won't ever work with Gatsby and they have no solutions available so mathRandom it is. I have the user reset their pass so it's temporary – Edward Jul 08 '22 at 18:18
  • 1
    @Edward DO NOT USE math.random. Your passwords will be inherently insecure and easy to guess. Your entire application will be insecure from the get-go. I’ve updated my answer for crypto-browserfy. It has a different api than node’s crypto – thecoolwinter Jul 09 '22 at 18:20
  • still getting an error with the updated snippet: process is not defined ` if (typeof cb === 'function') { return process.nextTick(function () { cb(null, bytes) }) } ` – Edward Jul 11 '22 at 15:33
  • @Edward sounds like your crypto installation went wonky. Maybe try that again? I don’t have experience with Gatsby but that would be my guess. – thecoolwinter Jul 11 '22 at 20:18
  • that's probably it since I jam npm i into my terminal from other SO posts until I stop getting errors. I'll clean up my dependencies, try [this solution](https://stackoverflow.com/questions/67333737/add-crypto-browserify-to-gatsby-project) and see if I get an error again. – Edward Jul 12 '22 at 17:16
  • Using `%` [introduces a bias in the distribution](https://dimitri.xyz/random-ints-from-random-bits/) – Ian Dunn Jan 18 '23 at 17:24
-1

To generate password with minimum 1 char from each array element

generatePassword(length:number){
  let password ="";
  const chars= [
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
    "abcdefghijklmnopqrstuvwxyz",
    "@$!%*?&",
    "1234567890"
  ];
  for(let j=0; j<chars.length; j++){
    password += chars[j].charAt(Math.floor(Math.random() * chars[j].length));
  }
  if(length > chars.length){
    length = length - chars.length;
    for(let i=0; i<length; i++){
      const index = Math.floor(Math.random() * chars.length);
      password += chars[index].charAt(Math.floor(Math.random() * chars[index].length));
    }
  }
  return  password.split('').sort(function(){return 0.5-Math.random()}).join('');
}
noiissyboy
  • 61
  • 2
  • 5
-1

I wrote this function and it should work for most cases.

 function generatePassword(length, options) {
    const optionsChars = {
        digits: "1234567890",
        lowercase: "abcdefghijklmnopqrstuvwxyz",
        uppercase: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
        symbols: "@$!%&"
    }
    const chars = [];
    for (let key in options) {
        if (options.hasOwnProperty(key) && options[key] && optionsChars.hasOwnProperty(key)) {
            chars.push(optionsChars[key]);
        }
    }

    if (!chars.length)
        return '';

    let password = "";

    for (let j = 0; j < chars.length; j++) {
        password += chars[j].charAt(Math.floor(Math.random() * chars[j].length));
    }
    if (length > chars.length) {
        length = length - chars.length;
        for (let i = 0; i < length; i++) {
            const index = Math.floor(Math.random() * chars.length);
            password += chars[index].charAt(Math.floor(Math.random() * chars[index].length));
        }
    }

    return password;
}

const password = generatePassword(9, {digits: true, lowercase: true, uppercase: true, symbols: true});

console.log(password)
Dor Levi
  • 303
  • 2
  • 4
-2

There is a random password string generator with selected length

let input = document.querySelector("textarea");
let button = document.querySelector("button");
let length = document.querySelector("input");

function generatePassword(n) 
{
 let pwd = "";

  while(!pwd || pwd.length < n)
  {
   pwd += Math.random().toString(36).slice(-22);
  }
  
  return pwd.substring(0, n);
}

button.addEventListener("click", function()
{
 input.value = generatePassword(length.value);
});
<div>password:</div>
<div><textarea cols="70" rows="10"></textarea></div>
<div>length:</div>
<div><input type="number" value="200"></div>
<br>
<button>gen</button>
Profesor08
  • 1,181
  • 1
  • 13
  • 20