Node.jsの安全なランダムトークン


273

、この質問エリックはNode.js.で安全なランダムなトークンを生成する必要があります crypto.randomBytesランダムなバッファを生成するメソッドがあります。しかし、ノード内のbase64エンコードは、URLセーフではありません、それは、/+の代わりに-_。したがって、私が見つけたそのようなトークンを生成する最も簡単な方法は

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

よりエレガントな方法はありますか?


残りのコードは何ですか?
Lion789 2013

3
これ以上何も必要ありません。残りの何を見てみたいですか?
Hubert OG

気にしないで、私はそれを機能させました、あなたがそれをどのように
投入したの

1
恥知らずな自己プラグイン、私はさらに別のnpmパッケージを作成しました:tokgen。正規表現('a-zA-Z0-9_-')の文字クラスと同様の範囲構文を使用して、許可される文字を指定できます。
Max Truxa

1
これは、特定の長さの文字列が必要な場合に便利です。3/4は基本変換を処理するためのものです。/ *長さのbase64エンコードされた文字列を返します* / function randomString(length){return crypto.randomBytes(length * 3/4).toString( 'base64'); これらの文字数制限のあるデータベースに適しています。
TheUnknownGeek 2017年

回答:


353

crypto.randomBytes()を試してください:

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

「hex」エンコーディングはノードv0.6.x以降で機能します。


3
よさそうです、ありがとう!ただし、「base64-url」エンコーディングが適切です。
Hubert OG

2
ヒントをありがとう、しかし私はOPが単にすでに標準のRFC 3548セクション4「URLとファイル名セーフアルファベットを使用したBase 64エンコーディング」を望んでいたと思います。IMO、文字を置き換えることは「十分にエレガント」です。
natevw 2013年

8
上記をbashのワンライナーとしてお探しの場合は、次のことが可能ですnode -e "require('crypto').randomBytes(48, function(ex, buf) { console.log(buf.toString('hex')) });"
Dmitry Minkovsky

24
そして、いつでもbuf.toString('base64')Base64でエンコードされた数値を取得できます。
ドミトリーミンコフスキー

1
URLとファイル名に安全なアルファベットを使用したBase 64エンコーディングについては以下のこのアンサーを参照してください
Yves M.

232

私のようなJSエキスパートでない場合の同期オプション。インライン関数変数にアクセスする方法に時間を費やす必要があった

var token = crypto.randomBytes(64).toString('hex');

7
また、すべてをネストしたくない場合にも。ありがとう!
Michael Ozeryansky、2016年

2
これは間違いなく機能しますが、ほとんどの場合、jhの回答で非同期オプションを示す必要があることに注意してください。
Triforcey

1
const generateToken = (): Promise<string> => new Promise(resolve => randomBytes(48, (err, buffer) => resolve(buffer.toString('hex'))));
yantrab

1
@Triforceyで、通常は非同期オプションが必要な理由を説明できますか?
トーマス

2
@thomasランダムデータは、ハードウェアによっては計算に時間がかかる場合があります。場合によっては、コンピューターがランダムデータを使い果たすと、その場所に何かが返されるだけです。ただし、他の場合では、コンピュータがランダムデータの返送を遅らせる可能性があり(これは実際に必要なことです)、呼び出しが遅くなります。
Triforcey

80

0. nanoidサードパーティライブラリの使用[NEW!]

JavaScript用の小さくて安全な、URLフレンドリーでユニークな文字列IDジェネレーター

https://github.com/ai/nanoid

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


1. URLおよびファイル名セーフアルファベットを使用したBase 64エンコーディング

RCF 4648の7ページでは、URLを安全にしてbase 64でエンコードする方法について説明しています。あなたはbase64urlのような既存のライブラリを使用して仕事をすることができます。

関数は次のようになります。

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

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

使用例:

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

返される文字列の長さがsize引数と一致しないことに注意してください(size!=最終的な長さ)。


2.限られた文字セットの暗号ランダム値

このソリューションでは、生成されたランダム文字列が均一に分散されないことに注意してください。

そのような文字の限定されたセットから強力なランダム文字列を構築することもできます:

var crypto = require('crypto');

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

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

  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];
  }

  return result.join('');
}

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

使用例:

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

randomString(20, 'ABCDEFG');
// Returns 'CCBAAGDGBBEGBDBECDCE' which is 20 characters length.

2
@Lexynux ソリューション1(URLとファイル名セーフアルファベットを使用したBase 64エンコーディング)。これは、セキュリティの面で最も強力なソリューションだからです。このソリューションは、キーをエンコードするだけで、キーの生成プロセスを妨害しません。
Yves M.

ご協力ありがとうございました。コミュニティと共有する実用的な例はありますか?歓迎されますか?
alexventuraio

6
生成されたランダム文字列は均一に分散されていないことに注意してください。これを示す簡単な例は、長さが255の文字セットと文字列の長さが1の場合、最初の文字が出現する可能性が2倍高いことです。
フロリアンウェンデルボルン2016

@Dodekeractはい、あなたはソリューション2について話しています。それがソリューション1の方がはるかに強力な理由です
イブスM.

私は私の応答にnanoidサードパーティのライブラリを追加しましたgithub.com/ai/nanoid
イヴ・M.

13

ES 2016のasyncおよびawaitの標準を使用して非同期でこれを行う最新の正しい方法は、次のとおりです(ノード7以降)。

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

これは、Babel変換なしでNode 7の設定なしで機能します


この例を更新して、ここで説明するように名前付きパラメーターを渡す新しい方法を組み込みました:2ality.com/2011/11/keyword-parameters.html
real_ate

7

ランダムなURLとファイル名の文字列を安全(1ライナー)

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

その素晴らしさに素晴らしい答えがあります!イベントループが不確定な方法で停止する可能性があることに注意してください(負荷が大きく、時間に敏感なシステムで頻繁に使用される場合にのみ関連します)。それ以外の場合は同じことを行いますが、非同期バージョンのrandomBytesを使用します。nodejs.org/api/…を
Alec Thilenius 2016

6

チェックアウト:

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

いいね!絶対に過小評価されたソリューション。「length」の名前を「desiredLength」に変更し、使用する前に値で開始すると、すばらしいでしょう:)
Florian Blum

不思議に思う人にとって、ceiland slice呼び出しは奇数である必要な長さのために必要です。長さが同じであっても、何も変化しません。
Seth

6

async / awaitとpromisificationを使用します。

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

次のようなものを生成します VjocVHdFiz5vGHnlnwqJKN0NdeHcz8eM


4

real_atesES2016の方法を見てください。より正確です。

ECMAScript 2016(ES7)の方法

import crypto from 'crypto';

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

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

発電機/歩留まり

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

@Jeffpowrs確かに、Javascriptはアップグレードされています:) Lookup Promises and Generators!
K-SOの毒性が高まっています。

別のECMA7プロミスハンドラーを待つ
Jain

ほとんどの場合、「それを行うには正しい方法」に向かって動いているように私はあなたがこの上でES 2016年最初の例を作るべきだと思う
real_ate

Nodeに固有の回答を以下に追加しました(インポートではなくrequireを使用)。インポートを使用する特別な理由はありましたか?バベルを走らせていますか?
real_ate

@real_ate確かに、インポートが正式にサポートされるまで、CommonJSの使用に戻りました。
K-SOの毒性が高まっています。


2

npmモジュールanyidは、さまざまな種類の文字列ID /コードを生成するための柔軟なAPIを提供します。

48個のランダムなバイトを使用してA-Za-z0-9でランダムな文字列を生成するには:

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

ランダムなバイトで埋められた固定長のアルファベットのみの文字列を生成するには:

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

内部的にはcrypto.randomBytes()、ランダムを生成するために使用します。


1

これは、@ Yves Mの回答の上から逐語的に取得した非同期バージョンです。

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

1

URLセーフでbase64エンコードされたトークンを取得するシンプルな関数!上記の2つの回答を組み合わせたものです。

const randomToken = () => {
    crypto.randomBytes(64).toString('base64').replace(/\//g,'_').replace(/\+/g,'-');
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.