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

3
더 이상 필요하지 않습니다. 어떤 휴식을 원하십니까?
허버트 OG

신경 쓰지 마라, 나는 그것을 작동 시켰고, 당신이 그것을 어떻게 던 졌는지 확신 할 수 없었지만, 개념을 더 잘 이해했다
Lion789

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

답변:


353

crypto.randomBytes () 사용해보십시오 :

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

'16 진수 '인코딩은 노드 v0.6.x 이상에서 작동합니다.


3
감사합니다! 'base64-url'인코딩이 좋을 것입니다.
Hubert OG

2
팁에 감사하지만 OP는 단순히 표준 RFC 3548 섹션 4 "URL 및 파일 이름 안전 알파벳으로 Base 64 인코딩"을 원한다고 생각합니다. 문자를 바꾸는 IMO는 "충분히 우아하다".
natevw

8
위의 bash one-liner로 찾고 있다면 할 수 있습니다node -e "require('crypto').randomBytes(48, function(ex, buf) { console.log(buf.toString('hex')) });"
Dmitry Minkovsky

24
그리고 buf.toString('base64')Base64로 인코딩 된 숫자를 얻기 위해 언제든지 할 수 있습니다.
Dmitry Minkovsky

1
URL 및 파일 이름 안전 알파벳을 사용한 기본 64 인코딩에 대해서는 아래이 답변을 참조하십시오.
Yves M.

232

나와 같은 JS 전문가가 아닌 경우 동기 옵션. 인라인 함수 변수에 액세스하는 방법에 시간을 보냈습니다

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

7
또한 모든 것을 중첩하고 싶지 않은 경우. 감사!
Michael Ozeryansky

2
이것이 확실히 작동하는 동안 대부분의 경우 비동기 옵션이 jh의 답변에 표시되기를 원합니다.
Triforcey 2016 년

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! = final length)와 일치하지 않습니다.


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의 문자열 길이에 대해 첫 번째 문자가 나타날 확률이 두 배라는 것입니다.
Florian Wendelborn

@Dodekeract 예, 당신은 솔루션 2에 대해 이야기하고 있습니다. 그래서 솔루션 1이 훨씬 강력합니다.
Yves M.


13

ES 2016 표준 async and 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);
}

이것은 바벨 변환없이 노드 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/...
알렉 Thilenius에게

6

체크 아웃 :

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

좋은! 절대적으로 과소 평가 된 솔루션입니다. "길이"의 이름을 "desiredLength"로 바꾸고 그것을 사용하기 전에 값으로 시작하면 좋을 것입니다 :)
Florian Blum

궁금한 사람 은 원하는 길이만큼 홀수 ceilslice통화가 필요합니다. 균일 한 길이의 경우 아무 것도 변경하지 않습니다.
세스

6

비동기 / 대기 및 약속 .

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

아래에 노드 고유의 답변을 추가했습니다 (가져 오기 대신 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 인코딩을 가진 토큰을 얻는 간단한 기능! 위의 두 가지 답변이 조합되어 있습니다.

const randomToken = () => {
    crypto.randomBytes(64).toString('base64').replace(/\//g,'_').replace(/\+/g,'-');
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.