367

In this question Erik needs to generate a secure random token in Node.js. There's the method crypto.randomBytes that generates a random Buffer. However, the base64 encoding in node is not url-safe, it includes / and + instead of - and _. Therefore, the easiest way to generate such token I've found is

require('crypto').randomBytes(48, function(ex, buf) {
    token = buf.toString('base64').replace(/\//g,'_').replace(/\+/g,'-');
});

Is there a more elegant way?

Community
  • 1
  • 1
Hubert OG
  • 19,314
  • 7
  • 45
  • 73
  • What is the rest of the code? – Lion789 Aug 03 '13 at 05:01
  • 7
    There's nothing more needed. What rest would you like to see? – Hubert OG Aug 12 '13 at 13:46
  • Nevermind, I got it to work, was just unsure of how you threw it in, but got a better grasp of the concept – Lion789 Aug 12 '13 at 19:22
  • I made an npm package: https://www.npmjs.com/package/secure-random-string – Simon May 08 '15 at 15:05
  • 1
    Shameless self-plug, I created yet another npm package: [tokgen](https://www.npmjs.com/package/tokgen). You can specify allowed characters using a range syntax similar to character classes in regular expressions (`'a-zA-Z0-9_-'`). – Max Truxa Aug 09 '16 at 19:18
  • [rand-token](https://github.com/sehrope/node-rand-token) works for me – Kunal Jul 14 '17 at 16:54
  • 1
    This may be convenient for anyone who'd like a specific string length. The 3/4th's is to handle the base conversion. /*returns a base64 encoded string of length*/ function randomString(length){ return crypto.randomBytes(length*3/4).toString('base64'); } Works nice for those databases with those character limits. – TheUnknownGeek Oct 19 '17 at 21:51
  • Sync: `require('crypto').randomBytes(48).toString('base64').replace(/\//g,'_').replace(/\+/g,'-'); ` – malix Feb 20 '18 at 21:21
  • This works well `node -e "require('crypto').randomBytes(68^12, function(ex, buf) { console.log(buf.toString('base64')) });"` – 0xe1λ7r Aug 12 '22 at 04:19

16 Answers16

463

Try crypto.randomBytes():

require('crypto').randomBytes(48, function(err, buffer) {
  var token = buffer.toString('hex');
});

The 'hex' encoding works in node v0.6.x or newer.

mikemaccana
  • 110,530
  • 99
  • 389
  • 494
thejh
  • 44,854
  • 16
  • 96
  • 107
  • 4
    That seems better, thanks! A 'base64-url' encoding would be nice, though. – Hubert OG Jan 13 '12 at 19:40
  • Tantek did some interesting work on that: http://tantek.pbworks.com/w/page/24308279/NewBase64 – greut Jan 14 '12 at 17:28
  • 2
    Thanks for the tip, but I think the OP simply wanted the already-standard RFC 3548 section 4 "Base 64 Encoding with URL and Filename Safe Alphabet". IMO, replacing the characters is "elegant enough". – natevw Oct 07 '13 at 21:58
  • 17
    If you're looking for the above as a bash one-liner, you can do `node -e "require('crypto').randomBytes(48, function(ex, buf) { console.log(buf.toString('hex')) });"` – Dmitry Minkovsky Feb 25 '14 at 18:00
  • 28
    And you can always do `buf.toString('base64')` to get a Base64-encoded number. – Dmitry Minkovsky Feb 25 '14 at 18:11
  • I made it into an npm module for easy installation. `npm -g install secretkey` – Ivan -Oats- Storck Jun 11 '14 at 04:29
  • @thejh Is using a base64 encoding safer than a hex encoding? – kittyminky Aug 15 '14 at 21:05
  • @kittyminky2 No, but it's shorter. – thejh Aug 16 '14 at 14:34
  • 1
    See [this answser](http://stackoverflow.com/a/25690754/1480391) below for **base 64 encoding** with URL and Filename Safe Alphabet – Yves M. Jul 10 '15 at 14:32
  • 7
    A slightly more compact version of Dmitry's excellent one-liner: `node -p "require('crypto').randomBytes(48).toString('hex');"` (subbing `base64` for `hex`) if desired – eddies Aug 06 '18 at 07:56
  • What are the chances it will repeat or generate the same token? – KumailR Jul 08 '20 at 12:27
  • 1
    I'm sorry is it just me who thinks limiting A-Z to only A-F, severely lowers surface of attack. Why are we limiting ourselves to hex highest alphabet? – Muhammad Umer Oct 28 '20 at 17:33
  • 2
    @MuhammadUmer, assuming you are still using all of the 48 bytes generated in this example, the hex string will simply be longer than the base64 one, but have the same amount of randomness – Patronics Mar 29 '21 at 06:57
342

Synchronous option in-case if you are not a JS expert like me. Had to spend some time on how to access the inline function variable

var token = crypto.randomBytes(64).toString('hex');
phoenix2010
  • 3,633
  • 1
  • 13
  • 10
  • 10
    Also in case you don't want to have everything nested. Thanks! – Michael Ozeryansky Oct 07 '16 at 16:50
  • 5
    While this definitely works, do note that in most cases you'll want the async option demonstrated in thejh's answer. – Triforcey Jun 10 '18 at 21:18
  • 2
    `const generateToken = (): Promise => new Promise(resolve => randomBytes(48, (err, buffer) => resolve(buffer.toString('hex'))));` – yantrab May 01 '19 at 04:21
  • 2
    @Triforcey can you explain why you usually would want the async option? – dcts Jul 16 '19 at 05:17
  • 2
    @thomas Random data can take a while to calculate depending on hardware. In some cases if the computer runs out of random data it'll just return something in it's place. However in other cases it's possible the computer will delay the return of random data (which is actually what you would want) resulting in a slow call. – Triforcey Jul 17 '19 at 15:38
  • Here's it explained well from Wikipedia: "The speed at which entropy can be harvested from natural sources is dependent on the underlying physical phenomena being measured. Thus, sources of naturally occurring "true" entropy are said to be blocking" https://en.m.wikipedia.org/wiki/Random_number_generation – Triforcey Jul 17 '19 at 15:44
  • You can also use promisify function from the built in "util" module to avoid callback function. const token = await promisify(crypto.randomBytes)(48); Note that await can only be used in async function. – Eyal Ovadya Jul 26 '21 at 14:44
108

1. Using nanoid third party library [NEW!]


A tiny, secure, URL-friendly, unique string ID generator for JavaScript

https://github.com/ai/nanoid

import { nanoid } from "nanoid";
const id = nanoid(48);

2. Base 64 Encoding with URL and Filename Safe Alphabet


Page 7 of RCF 4648 describes how to encode in base 64 with URL safety.

This is natively supported by Node.js >=v14.18.0:

const crypto = require("crypto");

/** Sync */
function randomStringAsBase64Url(size) {
  return crypto.randomBytes(size).toString("base64url");
}

Usage example:

randomStringAsBase64Url(20);
// Returns "AXSGpLVjne_f7w5Xg-fWdoBwbfs" which is 27 characters length.

Note that the returned string length will not match with the size argument (size != final length).

If you are using Node.js <v14.18.0 you can use an existing library like base64url to do the job. The function will be:

const crypto = require("crypto");
const base64url = require("base64url");
    
/** Sync */
function randomStringAsBase64Url(size) {
  return base64url(crypto.randomBytes(size));
}

3. Crypto random values from limited set of characters


Beware that with this solution the generated random string is not uniformly distributed.

You can also build a strong random string from a limited set of characters like that:

const crypto = require("crypto");

/** Sync */
function randomString(length, chars) {
  if (!chars) {
    throw new Error("Argument 'chars' is undefined");
  }

  const charsLength = chars.length;
  if (charsLength > 256) {
    throw new Error("Argument 'chars' should not have more than 256 characters"
      + ", otherwise unpredictability will be broken");
  }

  const randomBytes = crypto.randomBytes(length);
  let result = new Array(length);

  let cursor = 0;
  for (let i = 0; i < length; i++) {
    cursor += randomBytes[i];
    result[i] = chars[cursor % charsLength];
  }

  return result.join("");
}

/** Sync */
function randomAsciiString(length) {
  return randomString(length,
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
}

Usage example:

randomAsciiString(20);
// Returns "rmRptK5niTSey7NlDk5y" which is 20 characters length.

randomString(20, "ABCDEFG");
// Returns "CCBAAGDGBBEGBDBECDCE" which is 20 characters length.
Yves M.
  • 29,855
  • 23
  • 108
  • 144
  • 2
    @Lexynux **Solution 1** (Base 64 Encoding with URL and Filename Safe Alphabet) because it is the strongest solution in term of security. This solution only encode the key and does not interfere with the key production process. – Yves M. Dec 28 '15 at 13:28
  • Thanks for your support. Do you have any working example to share with the community? It will be welcomed? – alexventuraio Dec 29 '15 at 20:22
  • 9
    Beware that the generated random string is not uniformly distributed. An easy example to show this is, that for a character set of length 255, and a string length of 1, the chance of the first character appearing is twice as high. – Florian Wendelborn Jul 25 '16 at 04:09
  • 1
    @Dodekeract Yes, you are talking about solution 2.. That's why solution 1 is way more strong – Yves M. Jul 25 '16 at 08:41
  • I've added nanoid third party library in my response https://github.com/ai/nanoid – Yves M. May 09 '18 at 15:10
  • @FlorianWendelborn may I ask you explain a bit your point. Is there any difference between 255 char set and hex string of generated buffer? – Alexey Sh. Mar 28 '23 at 10:28
  • 2
    @AlexeySh. a bit hard to remember as this was nearly a decade ago by now. I think it’s about the danger of just using mod/division rest to hash which character you get out of the randomness. For example, if you roll a random byte (0-255) and then only provide 255 possible characters the random 0 = random 255 = first possible character, so it’s not uniform – Florian Wendelborn Apr 01 '23 at 00:25
  • 1
    For option 2, note that since Node v14.18.0 'base64url' is supported as a built-in encoding, so you can just `crypto.randomBytes(size).toString('base64url')` now. – Tim Perry Jul 04 '23 at 17:29
  • Yeah right @TimPerry I've open a Node.js issue ling time ago and they accepted it! https://github.com/nodejs/node/issues/26512 – Yves M. Jul 05 '23 at 14:22
21

The up-to-date right way to do this asynchronously using ES 2016 standards of async and await (as of Node 7) would be the following:

const crypto = require('crypto');

function generateToken({ stringBase = 'base64', byteLength = 48 } = {}) {
  return new Promise((resolve, reject) => {
    crypto.randomBytes(byteLength, (err, buffer) => {
      if (err) {
        reject(err);
      } else {
        resolve(buffer.toString(stringBase));
      }
    });
  });
}

async function handler(req, res) {
   // default token length
   const newToken = await generateToken();
   console.log('newToken', newToken);

   // pass in parameters - adjust byte length
   const shortToken = await generateToken({byteLength: 20});
   console.log('newToken', shortToken);
}

This works out of the box in Node 7 without any Babel transformations

real_ate
  • 10,861
  • 3
  • 27
  • 48
  • I've updated this example to incorporate the newer method of passing named parameters as described here: http://2ality.com/2011/11/keyword-parameters.html – real_ate Jun 08 '17 at 11:06
19

As of Node.js 14.18 and 15.7, url-safe base64 encoding support is built-in:

const token = crypto.randomBytes(48).toString('base64url');

If you want to use the async version (because the function may have to wait for entropy), it can be promisified to align better with modern patterns:

const randomBytesAsync = util.promisify(crypto.randomBytes);

const token = (await randomBytesAsync(48)).toString('base64url');
Inkling
  • 3,544
  • 4
  • 30
  • 44
14

Random URL and filename string safe (1 liner)

Crypto.randomBytes(48).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
Kedem
  • 274
  • 2
  • 5
  • A wonderful answer in it's simplicity! Just be aware that it could stall the event loop in an indeterministic way (only relevant if it's used often, in a somewhat loaded, time-sensitive system). Otherwise, do the same thing, but using the async version of randomBytes. See https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback – Alec Thilenius Sep 28 '16 at 19:50
11

With async/await and promisification.

const crypto = require('crypto')
const randomBytes = Util.promisify(crypto.randomBytes)
const plain = (await randomBytes(24)).toString('base64').replace(/\W/g, '')

Generates something similar to VjocVHdFiz5vGHnlnwqJKN0NdeHcz8eM

Markus Hedlund
  • 23,374
  • 22
  • 80
  • 109
10

Check out:

var crypto = require('crypto');
crypto.randomBytes(Math.ceil(length/2)).toString('hex').slice(0,length);
n4m31ess_c0d3r
  • 3,028
  • 5
  • 26
  • 35
sudam
  • 101
  • 1
  • 3
  • Nice! Absolutely underrated solution. Would be great if you rename "length" to "desiredLength" and initiate it with a value before using it :) – Florian Blum Jul 25 '17 at 21:07
  • For anyone wondering, the `ceil` and `slice` calls are necessary for desired lengths that are odd. For even lengths, they don't change anything. – Seth Sep 12 '18 at 20:42
8

crypto-random-string is a nice module for this.

const cryptoRandomString = require('crypto-random-string');
 
cryptoRandomString({length: 10});                          // => '2cf05d94db'
cryptoRandomString({length: 10, type: 'base64'});          // => 'YMiMbaQl6I'
cryptoRandomString({length: 10, type: 'url-safe'});        // => 'YN-tqc8pOw'
cryptoRandomString({length: 10, type: 'numeric'});         // => '8314659141'
cryptoRandomString({length: 6, type: 'distinguishable'});  // => 'CDEHKM'
cryptoRandomString({length: 10, type: 'ascii-printable'}); // => '`#Rt8$IK>B'
cryptoRandomString({length: 10, type: 'alphanumeric'});    // => 'DMuKL8YtE7'
cryptoRandomString({length: 10, characters: 'abc'});       // => 'abaaccabac'

cryptoRandomString.async(options) add .async if you want to get a promise.

Hasan Sefa Ozalp
  • 6,353
  • 5
  • 34
  • 45
4

Look at real_ates ES2016 way, it's more correct.

ECMAScript 2016 (ES7) way

import crypto from 'crypto';

function spawnTokenBuf() {
    return function(callback) {
        crypto.randomBytes(48, callback);
    };
}

async function() {
    console.log((await spawnTokenBuf()).toString('base64'));
};

Generator/Yield Way

var crypto = require('crypto');
var co = require('co');

function spawnTokenBuf() {
    return function(callback) {
        crypto.randomBytes(48, callback);
    };
}

co(function* () {
    console.log((yield spawnTokenBuf()).toString('base64'));
});
basickarl
  • 37,187
  • 64
  • 214
  • 335
  • @Jeffpowrs Indeed, Javascript is upgrading :) Lookup Promises and Generators! – basickarl Nov 16 '15 at 21:54
  • try await, another ECMA7 promise handler – Jain Jul 08 '16 at 06:50
  • I think that you should make the ES 2016 the first example on this as it is moving towards the "right way to do it" in most cases – real_ate May 09 '17 at 10:00
  • I added an answer of my own below that was specific to Node (using require instead of import). Was there a particular reason why you're using import? Do you have babel running? – real_ate May 09 '17 at 15:31
  • @real_ate Indeed I was, I've reverted to using CommonJS until import is officially supported. – basickarl May 09 '17 at 21:20
4

in your terminal just write

node -e "console.log(crypto.randomBytes(48).toString('hex'))"

Or in your code use:

const randomToken = () => {
   crypto.randomBytes(48).toString('hex');
}
Kvetoslav
  • 443
  • 3
  • 15
3

https://www.npmjs.com/package/crypto-extra has a method for it :)

var value = crypto.random(/* desired length */)
jsonmaur
  • 29
  • 1
  • 4
  • great! But isnt it `.randomString (length, charset)` (see [documentation](https://www.npmjs.com/package/crypto-extra#randomstring-length-charset)). So you could user for example `crypto.randomString(12)`. – dcts Jan 13 '20 at 18:32
3

The npm module anyid provides flexible API to generate various kinds of string ID / code.

To generate random string in A-Za-z0-9 using 48 random bytes:

const id = anyid().encode('Aa0').bits(48 * 8).random().id();
// G4NtiI9OYbSgVl3EAkkoxHKyxBAWzcTI7aH13yIUNggIaNqPQoSS7SpcalIqX0qGZ

To generate fixed length alphabet only string filled by random bytes:

const id = anyid().encode('Aa').length(20).random().id();
// qgQBBtDwGMuFHXeoVLpt

Internally it uses crypto.randomBytes() to generate random.

aleung
  • 9,848
  • 3
  • 55
  • 69
2

Simple function that gets you a token that is URL safe and has base64 encoding! It's a combination of 2 answers from above.

const randomToken = () => {
    crypto.randomBytes(64).toString('base64').replace(/\//g,'_').replace(/\+/g,'-');
}
dcts
  • 1,479
  • 15
  • 34
2

0 dependency free solution... Works in browsers, deno & nodejs (with new global web crypto)

const random = size => btoa(
  String.fromCharCode(
    ...crypto.getRandomValues(
      new Uint8Array(size)
    )
  )
).replaceAll('+', 'x').replaceAll('/', 'I').slice(0, size)

for (let i = 5; i--;) console.log(random(16))

All doe I would just have used one single uint8array \w predefined length and called crypto.getRandomValues whenever I need something uniq (and slice it if i have to) and never deal with strings or base64, base64 is just unnecessary overhead. (allocating lots of buffer to fast can be costly)

const buf256 = new Uint8Array(256)
const random = crypto.getRandomValues.bind(crypto, buf256)

for (let i = 5; i--;) random()//.slice()
Endless
  • 34,080
  • 13
  • 108
  • 131
0

You can use the random-token lib. it's very easy to use . :)

var randomToken = require('random-token').create('abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789');
var token = randomToken(16);

And also you can not use different salt

var randomToken = require('random-token');
var token = randomToken(16); // output -> d8d4kd29c40f021 ```
xMan
  • 1