본문으로 건너뛰기
yceffort
PostsSeriesTagsAbout🧪 Research
EN

Tweaks

theme
accent palette
film grain
minimal mode
BACK TO INDEX
◆ ESSAY
--min
--year
KOoriginal

mailMail icongithubtwitter
yceffort
•
© 2026
•
https://yceffort.kr
BACK TO INDEX
◆ ESSAY

자바스크립트에서 안전하게 난수 생성하는 방법

avatar
yceffort
2021-09-01 · 7분
7min
2021year
KOoriginal
javascriptsecurity

Table of Contents

  • Introduction
  • 자바스크립트에서 Math.random()을 사용해야 하는 경우
  • Math.random()의 보안 취약점
  • Web Crypto API
  • Web Crypto API 활용하기
    • 문법
  • 난수 생성하기

Introduction

애플리케이션을 개발하다보면, 안전하게 난수를 생성해야 하는 경우가 있다. 예를 들어 주사위 게임이나 추첨, private key 생성 등등, 안전하게 난수를 생성하는 방법을 알아두어야 한다.

일단, 자바스크립트에는 Math 객체에 random()이라는 메소드가 존재한다. 이 메소드를 사용하면, 랜덤한 숫자를 생성할 수 있다.

Math.random()에서는 0이상 1미만의 부동 소수점 난수를 리턴한다. 0≥x<10 \geq x \lt 10≥x<1

이 메소드를 사용하여 특정 범위의 랜덤한 숫자를 생성하는 다양한 방법이 있지만, 사실 Math.random()은 실제로 랜덤한 숫자롤 생성한다고 보기 어렵다. 이는 유사 난수 이기 때문이다. 알려진 것 처럼, 컴퓨터로 유사 난수가 아닌 진짜 난수를 생성하는 것은 어렵다.

일반적으로, Math.random()으로 생성한 유사 난수는 대부분의 경우 충분한 답이 될 수 있지만, 암호학적으로 안전한 난수를 생성할 필요도 존재한다. 즉, 패턴을 통해서 쉽게 추측할 수 없거나, 시간이 지나도 반복되지 않는 진짜 난수가 필요하다는 것이다.

자바스크립트에서 Math.random()을 사용해야 하는 경우

Math.random()은 이른바 '시드'라고 하는 내부의 숨겨진 값에서 만들어지는 비 암호화 랜덤 숫자를 리턴한다. 시드는 지정된 범위에서 균일하게 생성된 숨겨진 숫자 시퀀스의 시작점이다.

이 메소드의 가장 간단한 사용예제로는, 0과 1사이의 랜덤한 부동 소수점을 만드는 것이다.

const randomNumber = Math.random()
console.log(randomNumber) // 0.10150112695188218

이 랜덤한 숫자에 다른 숫자를 곱해서 원하는 크기의 결과를 만들어 낼 수 있다.

const max = 6
const randomNumber = Math.floor(Math.random() * max)
console.log(randomNumber) // 3

또 다른 사용사례로는, Math.floor를 사용하여 특정 범위내의 난수를 생성하는 것이다. floor는 특정 숫자 보다 작거나 같은 숫자를 리턴한다.

const max = 4
const min = 2

const result1 = Math.random() * (max - min)
console.log(result1) // 0.30347479463943516

const result2 = Math.random() * (max - min) + min
console.log(Math.floor(result2)) // 2

Math.random()의 보안 취약점

Math.random()은 앞서 언급한 것처럼 보안적인 측면에서 단점이 있다. MDN의 문서에 따르면 Math.random()는 암호학적으로 안전한 난수를 생성해주지 않는다. 따라서 프로그램의 보안과 관련된 로직에서는 Math.random()을 사용하지 않는 것이 좋다.

Note: 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.

그 원인은 아래와 같다.

  • 균일한 분포 내에서 랜덤 정수를 생성하는데 사용되는 로직이 부적절하고 일반적으로 편향되어 있음
  • 사용해야할 임의의 비트/바이트 수가 브라우저 별로 일치 하지 않음
  • 무작위 결과값은 항상 일관되게 다시 생성하기 어려우므로, 이는 본질적으로 비결정적이고 불규칙함
  • 빌트인 시드가 변조될 수 있으므로 무결성 측면에서 부적합

이러한 문제들 때문에, 월드와이드웹 컨소시움은 Web Crypto API를 만들어 공개하였다. 이 기능은 대부분의 브라우저에서 사용할 수 있다.

Web Crypto API

Web Crypto API는 window.crypto를 통해 엑세스할 수 있는 다양한 암호화 관련 메소드와 함수를 제공한다. 브라우저에서는, crypto.getRandomValues(Int32Array)를 사용하여 암호학적인 난수를 생성할 수 있다.

var array = new Uint32Array(10)
window.crypto.getRandomValues(array)

console.log('나의 행운의 숫자들:')
for (var i = 0; i < array.length; i++) {
  console.log(array[i])
}
// 나의 행운의 숫자들:
// 4213312451
// 4055435872
// 1248983520
// 2190329984
// 3226059214
// 1665817179
// 745131913
// 3947493810
// 218658595
// 2076931579

Nodejs에서는 표준 web crypto api가 제공된다. require('crypto').randomBytes(size)를 사용하면, node에 있는 native 암호화 모듈을 사용하여 난수를 생성할 수 있다.

const randomBytes = require('crypto').randomBytes(2)
const number = parseInt(randomBytes.toString('hex'), 16)

console.log(number) // 40358

Web Crypto API에 사용되는 의사 난수 생성 알고리즘 (pseudo-random number generator algorithm, PRNG)는 브라우저에 따라서 다를 수 있다.

Web Crypto API 활용하기

Crypto.getRandomValues() 메소드는 암호학적으로 강력한 난수를 리턴한다. 대부분의 웹 브라우저에서 사용할 수 있으며, 구현 방식에 따라 차이가 있을 수 있지만 엔트로피가 충분한 시드를 사용해야 한다. 이는 성능과 보안에 부정적인 영향을 미치지 않기 위함이다.

getRandomValues()는 crypto 인터페이스 중 유일하게 안전하지 않는 컨텍스트에서 사용할 수 있는 메소드다. 따라서 여기서 얻은 암호화 키는 안전한 결과가 아닐 수도 있으므로, 암호화 키를 생성할 때 이 메소드를 사용하지 않는 것이 좋다. 이 경우에는, generateKey() 메소드를 사용하는 것이 좋다.

문법

Web Cryptography API는 바이트 시퀀스를 나타내는 입력으로 ArrayBuffer, TypedArray를 인수로 받는다.

cryptoObj.getRandomValues(typedArray)

typedArray는 정수 기반의 TypedArray객체다. 이 외에도 Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array가 될 수 있다. 이 배열이 이제 랜덤한 난수로 채워지게 된다.

난수 생성하기

보안 목적으로 필요한 모든 임의의 값 (공격자에게 공격 받을 수 있는 가능성이 있는 모든 값)는 암호학적으로 안전한 의사 난수 생성기 (Cryptographically Secure Pseudo-Random Number Generator, CSPRNG)를 사용하여 생성해야 한다.

이를 활용할 수 있는 분야로는 토큰 확인 또는 리셋, 복권 번호, API 키, 암호 생성, 암호화 키 등이 있다.

가장 안전하게 생성할 수 있는 방법은 무엇일까? 가장 좋은 방법은 보안상으로 잘 설계 되어 있는 라이브러리를 활용하는 것이다. Nodejs를 기준으로 살펴보면,

  • random-number-csprng
  • API Key, 토큰에는 uuid, uuid.v4

관련 글

  • #nodejs#security#javascript

    Node.js vm 모듈의 함정: 샌드박스가 아닌 이유

    집필 중인 Node.js Deep Dive의 5.2장(vm 모듈의 함정) 일부를 미리 공개합니다.

    2026-02-27·25분
  • ◆ OG 스크래핑 서버 설계 노트
    #nodejs#security#ssrf

    OG 스크래핑 서버를 Node.js로 짓는다면 (2): SSRF는 어떻게 뚫리는가

    사용자가 준 URL을 서버가 대신 여는 기능은 SSRF의 교과서적 조건을 명세로 갖고 있다. 화이트리스트를 뚫는 우회 여섯 가지를 먼저 보고, 그것을 막는 방어 원리 다섯 개를 Node에서 실제로 돌려본 기록. 손으로 IPv4-mapped를 벗기면 16진 표기에서 뚫리고, undici의 lookup 훅은 호스트가 IP 리터럴이면 아예 호출되지 않으며, URL.hostname은 IPv6 리터럴의 대괄호를 남긴다. OG 스크래핑 서버 설계 노트 2부작의 마지막 편이다.

    2026-08-22·39분
  • #turbopack#nextjs#bundler

    Next.js turbopack에서 싱글톤이 두 개가 됐다: scope hoisting 버그와 순환 import

    Next.js 16 turbopack 프로덕션 빌드에서 모듈 스코프 싱글톤이 런타임에 두 개가 됐다. 같은 동기 구간에서 조건 판정이 뒤집히고, 응답이 도착해도 타임아웃이 나는 증상을 번들 산출물로 추적한 기록. scope hoisting의 부분 병합, 순환 import, 이미 고쳐져 있던 upstream 버그, 그리고 뒤늦게 돌린 단일 변수 실험까지.

    2026-08-19·28분
  • #javascript#animation#web-animations-api

    number-flow를 구형 브라우저로 이식하기: 다섯 가지 결정과 두 가지 번복

    number-flow가 애니메이션을 켜는 최소 버전은 Chrome 125, Safari 17.2다. 이 하한을 Chrome 66과 WebKit 16.4까지 내리는 포크를 만들면서 내린 결정들과, 뒤집게 된 판단 두 가지, 그리고 자동 강등을 포기한 Safari 버그 조사의 기록.

    2026-08-11·44분

새 글을 놓치고 싶지 않으시다면 RSS로 구독해 주세요.

RSS 구독 →

yceffort — 프론트엔드 엔지니어입니다. 발표·기술 자문·기고 문의는 이곳에서 받고 있습니다.

← Back to the blogIssue on GitHub →