简体   繁体   中英

Secure random token in Node.js

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?

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.

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');

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. You can use an existing library like base64url to do the job.

The function will be:

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

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

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).


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:

var 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.

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

随机 URL 和文件名字符串安全(1 行)

Crypto.randomBytes(48).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');

Check out:

var crypto = require('crypto');
crypto.randomBytes(Math.ceil(length/2)).toString('hex').slice(0,length);

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

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')); });

https://www.npmjs.com/package/crypto-extra有一个方法:)

var value = crypto.random(/* desired length */)

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.

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 .

Here is an async version taken verbatim from above @Yves M.'s answer

var crypto = require('crypto');

function createCryptoString(length, chars) { // returns a promise which renders a crypto string

    if (!chars) { // provide default dictionary of chars if not supplied

        chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    }

    return new Promise(function(resolve, reject) {

        var charsLength = chars.length;
        if (charsLength > 256) {
            reject('parm chars length greater than 256 characters' +
                        ' masks desired key unpredictability');
        }

        var randomBytes = crypto.randomBytes(length);

        var result = new Array(length);

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

        resolve(result.join(''));
    });
}

// --- now generate crypto string async using promise --- /

var wantStringThisLength = 64; // will generate 64 chars of crypto secure string

createCryptoString(wantStringThisLength)
.then(function(newCryptoString) {

    console.log(newCryptoString); // answer here

}).catch(function(err) {

    console.error(err);
});

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,'-');
}

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 ```

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');

in your terminal just write

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

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))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM