Go 1.22 버전의 보안 난수 생성 기능 강화
(go.dev)- Go 1.22는
math/rand와math/rand/v2의 기본 난수 소스를 암호학적으로 강한 생성기로 바꿔,crypto/rand를 써야 할 곳에서 실수했을 때의 피해를 크게 줄임 - 기존 Go 1 생성기는 607개의
uint64상태를 쓰는 선형 피드백 시프트 레지스터라, 607개 출력만 관측해도 과거·미래 값을 복원할 수 있음 math/rand/v2의 PCG-DXSM은 통계적 난수 품질과 상태 크기를 개선했지만, 비밀값에 필요한 예측 불가능성까지 보장하지는 못함- 새 ChaCha8Rand는 32바이트 시드, 16블록마다 재키잉, 코어별 300바이트 상태를 사용하며
math/rand/v2, 일부math/rand, map 해시 시드에 적용됨 - 성능 비용은 제한적임: ChaCha8Rand는 Go 1 생성기보다 느리지만 최대 2배 이내이고, 일반 서버에서는 차이가 3ns를 넘지 않아 대부분의 프로그램에서 보안 이득이 더 큼
Go 1.22가 바꾼 난수 기본값
- Go 1.22는
math/rand와math/rand/v2에서 암호학적으로 강한 의사난수 생성기를 사용하도록 기본값을 바꿈 - 목적은
crypto/rand가 필요한 곳에 개발자가 실수로math/rand를 사용했을 때의 피해를 줄이는 데 있음 - Go의 난수 API는 전통적으로 두 범주로 나뉨
math/rand: 시뮬레이션, 샘플링, 수치해석, 비암호학적 무작위 알고리듬, 퍼징, 셔플, 지수 백오프 등에 쓰는 통계적 난수crypto/rand: 키·토큰처럼 예측 불가능성이 필요한 암호학적 난수
통계적 난수가 충분하지 않은 이유
- 통계적 난수 생성기는 기본 통계 테스트를 통과하면 여러 비암호학적 용도에 충분할 수 있음
- 그러나 알고리듬을 아는 관찰자가 충분한 출력을 보면 이후 시퀀스를 예측할 수 있는 경우가 많음
- Unix V3의
srand와rand는 이후 C와 여러 언어의 난수 API에 영향을 준 초기 형태임- 단일 정수 시드로 상태를 설정함
- 선형 합동 생성기(LCG) 방식으로 다음 값을 계산함
- 내부 상태가 단순해 출력 하나만으로도 미래 값을 쉽게 계산할 수 있음
- LCG는 가능한 출력값을 반복 전 한 번씩 내보내도록 상수를 고를 수 있지만, 낮은 비트가 짧은 주기로 반복되는 약점이 있음
Go 1 생성기의 구조와 취약성
- Go 1의
math/rand생성기는 선형 피드백 시프트 레지스터 계열임 - 내부 상태는 607개의
uint64로 된vec슬라이스임vec[606]은 “tap”vec[334]는 “feed”- 다음 값을 만들 때 tap과 feed를 더해
x를 만들고,x를 feed 위치에 저장한 뒤 반환함
- 실제 구현은 슬라이스 전체를 이동하지 않고 tap과 feed 위치만 뒤로 이동해 비용을 줄임
- 다음 값 생성에는 두 번의 뺄셈, 두 번의 조건부 덧셈, 두 번의 로드, 한 번의 덧셈, 한 번의 저장이 필요함
- 반환값이 내부 상태 벡터의 한 원소라서 607개 출력을 읽으면 전체 상태가 노출됨
- 같은
vec을 채우고 알고리듬을 실행하면 미래 값을 예측할 수 있음 - 알고리듬을 거꾸로 실행하면 과거 값도 복원 가능함
- 같은
- Go 1 생성기는 보안 목적이 아니며, 생성된 수의 품질도 초기
vec설정에 의존함
PCG가 개선한 점과 남은 한계
math/rand/v2는 더 현대적인 통계적 난수 생성기로 Melissa O’Neill의 PCG를 채택함- Go의 PCG는 128비트 LCG를 기반으로 하며,
scramble함수로 128비트 상태를 64비트 출력으로 줄임 - Go는 제안 논의 중 O’Neill의 제안을 바탕으로 곱셈 기반
scramble을 사용함- 이 형태는 PCG-DXSM으로 불림
- Numpy도 이 PCG 형태를 사용함
- PCG는 Go 1 생성기보다 상태가 훨씬 작음
- Go 1 생성기: 607개의
uint64 - PCG: 두 개의
uint64
- Go 1 생성기: 607개의
- PCG는 초기 상태 값에 덜 민감하고 여러 통계 테스트를 통과하지만, 예측 불가능성을 보장하지는 않음
- PCG-XSL-RR은 되돌릴 수 있음
- PCG-DXSM도 되돌릴 수 있어도 놀랍지 않다고 봄
- 비밀값 생성에는 PCG가 아니라 다른 생성기가 필요함
암호학적 난수와 운영체제의 역할
- 암호학적 난수는 생성 방식을 알고 과거 출력값을 많이 본 관찰자에게도 실질적으로 예측 불가능해야 함
- 암호 프로토콜, 비밀 키, 현대 상거래, 온라인 프라이버시 등은 암호학적 난수에 의존함
- 실제 난수 공급은 운영체제가 담당함
- 마우스, 키보드, 디스크, 네트워크 타이밍 등 물리적 장치에서 무작위성을 수집함
- 최근에는 CPU가 직접 측정한 전기적 노이즈도 활용됨
- 운영체제가 충분한 난수, 예를 들어 최소 256비트를 모으면 해시나 암호화 알고리듬으로 긴 난수 시퀀스를 만듦
- 과거에는
/dev/random같은 장치 파일을 주로 썼지만, 현재 운영체제는 직접 시스템 호출을 제공함 - Go의
crypto/rand는 운영체제별 차이를 감추고 동일한 인터페이스인rand.Read를 제공함
ChaCha8Rand 설계
- Go 1.22의 새 생성기 ChaCha8Rand는 Daniel J. Bernstein의 ChaCha 스트림 암호를 가볍게 수정한 형태임
- ChaCha는 TLS와 SSH에서도 쓰이는 ChaCha20 형태가 널리 사용됨
- Jean-Philippe Aumasson의 Too Much Crypto는 8라운드 형태인 ChaCha8도 안전하다고 보며, ChaCha8은 대략 2.5배 빠름
- ChaCha8Rand는 ChaCha8을
rand.Source로 쓰기 위해 생성 블록을 입력과 XOR하지 않고 직접 난수 스트림으로 사용함- 이는 모두 0인 데이터를 암호화하거나 복호화하는 것과 같음
ChaCha8Rand의 변경점
- ChaCha8Rand는 32바이트 시드를 ChaCha8 키로 사용함
- ChaCha8은 64바이트 블록을 생성하며, 계산은 블록을 16개의
uint32로 취급함 - 일반 구현은 SIMD 명령으로 4개 블록을 동시에 계산할 수 있지만, XOR 입력에 쓰려면 interleaved 블록을 다시 풀어야 함
- ChaCha8Rand는 이 interleaved 블록 자체를 난수 스트림으로 정의해 unshuffle 비용을 없앰
- ChaCha8의 블록 마무리 단계에서는 특정 값을 각
uint32에 더함- 절반은 키 재료이고 절반은 알려진 상수임
- ChaCha8Rand는 알려진 상수를 다시 더하지 않아 최종 덧셈의 절반을 제거함
- 매 16번째 생성 블록마다 마지막 32바이트를 다음 16블록의 키로 사용함
- 이 재키잉은 일종의 전방향 안전성을 제공함
- 생성기 메모리 상태 전체가 노출되어도 마지막 재키잉 이후 값만 복구 가능하고, 과거 값에는 접근할 수 없음
- Go는 ChaCha8Rand C2SP 명세와 테스트 케이스를 공개해, 같은 시드에 대해 다른 구현도 Go 구현과 반복성을 공유할 수 있게 함
표준 라이브러리 적용 위치
- Go 런타임은 운영체제가 공급한 암호학적 난수로 시드된 코어별 ChaCha8Rand 상태를 유지함
- 각 코어당 상태 크기는 300바이트임
- 16코어 시스템에서는 단일 공유 Go 1 생성기 상태 4,872바이트와 비슷한 수준임
- 코어별 상태 덕분에 락 경합 없이 빠르게 난수를 만들 수 있음
math/rand/v2의 패키지 함수는 항상 ChaCha8Rand를 사용함- 예:
rand.N,rand.Float64
- 예:
math/rand의 패키지 함수는rand.Seed가 호출되지 않은 경우 ChaCha8Rand를 사용함- 예:
rand.Intn,rand.Float64 rand.Seed가 호출되면 호환성을 위해 Go 1 생성기로 되돌아가야 함
- 예:
- 런타임은 새 map의 해시 시드를 이전의 wyrand 기반 생성기 대신 ChaCha8Rand로 선택함
- 공격자가 map 구현의 특정 해시 함수를 알면 입력을 준비해 map을 이차 시간 동작으로 몰 수 있음
- 전역 시드 하나가 아니라 map별 시드를 쓰면 다른 퇴화 동작도 피할 수 있음
- map 시드에 암호학적 난수가 반드시 필요한지는 명확하지 않지만, 전환은 간단하고 신중한 선택이었음
- 별도 ChaCha8Rand 인스턴스가 필요한 코드는
rand.ChaCha8을 직접 만들 수 있음
보안 실수의 피해 축소
- Go는 보안 문제가 있는 흔한 실수를 줄이거나 제거해 기본적으로 안전한 코드 작성을 돕는 것을 목표로 함
- Go 1.20에서
math/rand의Read가 deprecated되자, 일부 개발자는 키 재료 생성처럼crypto/rand가 필요한 위치에math/rand를 쓰고 있었다는 사실을 발견함 - Go 1.20에서는 이런 실수가 심각한 보안 문제였음
- 키가 어디에 쓰였는지
- 키가 어떻게 노출됐는지
- 다른 난수 출력이 공격자에게 키 유도 단서를 줬는지 조사해야 했음
- Go 1.22에서는 같은 실수가 여전히 실수이지만 보안 참사로 이어질 가능성은 줄어듦
- 그래도 비밀값에는
crypto/rand를 쓰는 편이 더 나음- 운영체제 커널은 난수 값을 더 잘 보호할 수 있음
- 커널은 생성기에 새 엔트로피를 계속 추가함
- 커널 구현은 더 많은 검토를 받았음
암호처럼 보이지 않는 사례
- 무작위 UUID 생성은 UUID가 비밀값이 아니므로
math/rand로 충분해 보일 수 있음 - 하지만
math/rand를 현재 시간으로 시드하면, 서로 다른 컴퓨터가 같은 순간 실행될 때 같은 값을 만들 수 있음- 현재 시간이 밀리초 정밀도만 제공되는 시스템에서는 이 가능성이 더 커짐
- Go 1.20의 OS 엔트로피 기반 자동 시딩이 있어도, Go 1 생성기의 시드는 63비트 정수뿐임
- 시작 시 UUID를 만드는 프로그램은 가능한 첫 UUID가 2⁶³개로 제한됨
- 약 2³¹개 UUID 이후 충돌 가능성이 생김
- Go 1.22의 ChaCha8Rand는 256비트 엔트로피로 시드됨
- 가능한 첫 UUID는 2²⁵⁶개임
- 충돌 걱정이 필요 없음
- 프런트엔드 서버가 요청을 백엔드 서버에 무작위 배정하는 부하 분산도 예측 불가능한 난수가 필요할 수 있음
- 공격자가 배정을 관찰하고 예측 가능한 알고리듬을 안다면, 비싼 요청을 특정 백엔드에 몰 수 있음
- Go 1 생성기에서는 드물지만 가능한 문제임
- Go 1.22에서는 문제가 되지 않음
성능 특성
- ChaCha8Rand의 보안 이점에는 작은 비용이 있지만, 성능은 Go 1 생성기와 PCG와 같은 범위에 있음
- 비교 대상 연산은 두 가지임
Uint64: 난수 스트림에서 다음uint64반환N(1000):[0, 1000)범위의 난수 반환
- 64비트 x86 칩에서
GOARCH=386으로 빌드해 32비트 모드로 실행하면, PCG의 128비트 곱셈 때문에 PCG가 ChaCha8Rand보다 느림- ChaCha8Rand는 32비트 SIMD 산술을 사용함
- 일부 시스템에서는
Go 1: Uint64가PCG: Uint64보다 빠르지만,Go 1: N(1000)은PCG: N(1000)보다 느림- Go 1의
N(1000)은 범위 축소에 64비트 정수 나눗셈 두 번을 사용함 - PCG와 ChaCha8의
N(1000)은 대부분 나눗셈을 피하는 더 빠른math/rand/v2알고리듬을 사용함
- Go 1의
- 전체적으로 ChaCha8Rand는 Go 1 생성기보다 느리지만 2배 이상 느려지지는 않음
- 일반적인 서버에서는 차이가 3ns를 넘지 않으며, 이 차이가 병목이 되는 프로그램은 매우 적음
결론
- Go 1.22는 코드 변경 없이 프로그램의 보안을 높임
- 핵심 방식은
crypto/rand대신math/rand를 실수로 쓰는 흔한 문제를 줄이기 위해math/rand자체를 강화한 것임 - npm
keypair패키지처럼 Web Crypto API가 없을 때 JavaScriptMath.random으로 RSA 키 쌍 생성을 시도하는 사례도 있음 - 시스템 보안은 개발자가 실수하지 않는다는 전제에 의존할 수 없음
- Go 1.22의 ChaCha8Rand는 “수학적” 난수에도 암호학적으로 강한 의사난수 생성기를 쓰는 접근이 다른 생성기와 경쟁 가능한 성능을 낼 수 있음을 보여줌
댓글과 토론
Hacker News 의견들
-
기사에 나온 내용처럼, rclone에서 정확히 이 실수를 저질렀음
crypto/rand의Read를 쓰던 코드를 리팩터링하다가 import가 자동으로 바뀌었고, 아마math/rand를 쓰는 코드와 섞이면서goimports가math/rand로 바꾼 듯함
그 결과 보안 난수 생성기가 아니라, rclone이 시각으로 시드한 결정적 생성기를 쓰게 됐고 diff에서 알아채지 못했음 :-(
https://www.cvedetails.com/cve/CVE-2020-28924/
그래서 이번 변경에는 크게 찬성함- 아프네요, 미안합니다. 2016년에
goimports가crypto/rand를 선호하도록 바꿨기 때문에 리팩터링 중 무슨 일이 있었는지는 확실하지 않음
아마 같은 파일에math/rand전용 API를 쓰는 코드가 들어갔을 수도 있음
https://go-review.googlesource.com/24847
어쨌든 이런 부분을 정리하게 되어 다행임 - 나도
math/rand가 쓰였다고 착각해서 취약점을 제보받은 적이 있음. 실제로는 아니었고, 여러 파일을 헷갈린 것뿐이라 큰일은 아니었지만, 이 전체가 얼마나 혼란스러운지 보여줌
text/template과html/template도 비슷함. 돌이켜보면 이런 패키지 이름 가리기는 나쁜 아이디어였음 "secure password generation golang"으로 검색해보면 거의 모든 예제가math/rand를 쓰는 것도 봤음
더 나쁘게는 전부 비밀번호를 만들기 직전에 현재 시각으로 시드를 초기화함
우리 코드에서 누군가math/rand를 쓴 걸 발견한 뒤, 어디서 복사했는지 궁금해서 찾아보다 알게 됐음goimports는 거의 처음부터math/rand.Read와crypto/rand.Read를 특별 처리했음
다만 2016년의 https://github.com/golang/tools/commit/0835c735343e0d8e375f0...를 보면"rand.Read"가"math/rand"로 해석될 수 있던 시기가 언급됨
아마 그 시기에 걸렸던 것일 수도 있음"PredictableRand"같은 이름의 API 호출을 제공하는 게 그렇게 어렵지는 않을 것 같음
- 아프네요, 미안합니다. 2016년에
-
지난주에도 spacey가 https://news.ycombinator.com/item?id=40237491에 올렸지만, 그 글은 https://news.ycombinator.com/item?id=40224864의 중복으로 잘못 묻힌 듯함
두 go.dev 블로그 글은 같은 시리즈의 두 편이지만 꽤 다름. 이번 글은 효율적인 보안 난수 생성 알고리즘에 관한 것이고, 이전 글은 Go API 설계에 관한 글임 -
Russell Cox는 꾸준히 훌륭한 기술 블로그와 제안서, 그리고 작업물을 내놓음
글쓰기와 사고의 명료함을 높이고 싶다면 Russell Cox부터 보는 게 좋은 출발점임- 그의 유한 상태 오토마타와 정규식 시리즈 덕분에 그 분야에 푹 빠졌음
당시에는 Russ Cox가 누구인지도 몰랐지만, 그 글 시리즈는 정말 대단했음
정규식 구현에 관해 무료로 볼 수 있는 자료 중 아마 최고 품질이고, 그다음은 여러 컴파일러 중심 책들이지만 무료도 아니고 웹에서 쉽게 검색하기도 어려움 - 영상 데모도 잘 만듦 https://research.swtch.com/acme
- 그의 유한 상태 오토마타와 정규식 시리즈 덕분에 그 분야에 푹 빠졌음
-
crypto/rand가 반드시 필요한 곳에서math/rand를 써버린 적이 있음
그 결과 dnscrypt-proxy2 초기 버전에서 정적 키가 사용됐음
원인은 import를 자동으로 추가하는 VSCode 확장 기능이었음. 보안 난수가 필요한 모든 소스 파일에는crypto/rand를 직접 조심해서 import했지만, 파일 하나에서 빠뜨렸고, 모든 것이 컴파일되고 잘 동작했으며, 그 특정 파일에 확장이 조용히math/randimport를 추가한 걸 알아채지 못했음
그 뒤로는 잘못된rand가 자동 import되는 걸 피하려고crypto/rand를cryptorand라는 별칭으로 import함
참고로 Zig도 ChaCha8 기반 난수 생성기를 쓰며, 암호 연산에서는 사용자가 자기 생성기를 공급할 수 없고 항상 안전한 생성기가 사용됨. 테스트용으로는 일부 함수가 명시적 시드를 받음
제약 환경을 위해 표준 라이브러리에는 Ascon 순열과 Reverie 구성에 기반한 더 작은 생성기도 들어 있음- 당신의 경우 정확히 무슨 일이 있었는지는 모르겠지만, 아마 설명한 것과는 달랐을 가능성이 큼
2016년에goimports가math/rand보다crypto/rand를 선호하도록 바꿨고(https://go-review.googlesource.com/24847), 그때는 Go용 VSCode 지원이 나오기 전이었음 - 다른 사람도 같은 얘기를 했음. 솔직히 import를 자동으로 추가하는 관행은 완전히 이상해 보이고, 이름을 서로 다른 이름공간으로 분리하는 목적 자체를 무너뜨림
- 당신의 경우 정확히 무슨 일이 있었는지는 모르겠지만, 아마 설명한 것과는 달랐을 가능성이 큼
-
2020년대에도 여러 프로그래밍 언어의 기본 난수 구현이 왜 LFSR, MT 같은 빠른 난수 생성기를 쓰는지 자주 생각했음
사람들이 의사 난수 생성기가 필요한지 암호학적으로 안전한 의사 난수 생성기가 필요한지 모른다고 보는 쪽으로 보수적으로 잡고, 기본값을 후자로 바꾸며 전자가 필요한 사람만 명시적으로 선택하게 하는 편이 더 좋아 보임- PHP 8.2의 새 객체지향 난수 API에서는 정확히 그렇게 했음
개발자가 사용할 난수 엔진을 명시적으로 고르지 않으면 암호학적으로 안전한 생성기를 받게 됨
이제 어려운 부분은 사람들을 새 API로 옮기도록 설득하는 것임. 더 나아가 전역Mt19937인스턴스를 쓰는mt_rand()에서, PHP 7.0부터 이미 제공되는 CSPRNG 기반random_int()로 옮기는 것조차 쉽지 않음
[1] https://www.php.net/releases/8.2/en.php#random_extension - 최근 복잡한 자료 구조의 여러 구성요소에 임의 ID를 생성하는 새 Go 라이브러리를 쓰기 시작했음
내 사용 사례에는 구성요소가 수만 개 있었고, 프로파일링해보니 자료 구조 초기화 시간의 상당 부분이crypto/rand의Read()에 쓰였으며, 내 MacBook에서는 시스템 호출을 실행하고 있었음
라이브러리를 패치해math/rand의Read()를 쓰게 하자 성능이 크게 좋아졌음
math/rand가 더 빠른 것 외에도, 별 이유 없이 시스템의 엔트로피 풀을 고갈시킬까 걱정됐음. 이 경우 ID가 임의적이어야 할 유일한 이유는 자료 구조를 직렬화/역직렬화한 뒤 나중에 구성요소를 더 추가하는 것뿐인데, 나는 그럴 생각이 없었음
이 블로그에 나온 변경 시점이 내 경험과 정확히 어떻게 맞물리는지는 모르겠음. 아마 내가 예전 버전 라이브러리를 썼고, 지금은crypto/rand가 사실상math/rand와 구분 안 될 정도라면, 뭐 좋다고 봄 :-) - CSPRNG, 여기서는 ChaCha8을 쓰는 더 좋은 논거 중 하나는 벤치마크에서 PCG 대비 2배 이내라는 점임
상태 크기는 여전히 비교적 큼(64바이트 대 16바이트)이지만,mt19937이나 예전 Go PRNG 같은 것보다는 훨씬 낫음
CSPRNG가 훨씬 느리다면, 축소 라운드 ChaCha 변형이 아닌 일반 CSPRNG에서는 대체로 그렇듯, 기본값으로서 매력이 줄어듦 - 전자가 필요한 다른 경우가 뭐가 있을까? 재현 가능한 결과가 필요한 고정 시드 정도만 떠오름. 예를 들면 테스트나 검증 같은 것
시드가 필요 없는데도 사람들이 PRNG 쪽으로 밀리는 작은 요인이 하나 더 있음. CSPRNG API에는 시스템 호출 실패나 엔트로피 부족에 대비해 항상 처리해야 할 오류가 들어감
crypto/rand읽기는 실제로 얼마나 자주 실패할까? 현대 시스템에서 엔트로피를 고갈시키려면 얼마나 많이 읽어야 할까? 수십억 요청에서도 실패를 본 적이 없고,dd도 잘 됨
대부분의 사용 사례에는Must/panic스타일 API가 기본값으로 맞을지도 궁금함
덧붙여 Python의secrets패키지(https://docs.python.org/3/library/secrets.html)를 봤는데, 예외를 던질 수 있다는 언급이 하나도 없음. 실무에서는 그냥 안 일어나는 일인가? - “시스템의 모든 난수는 명시적으로 빠져나가지 않는 한 CSPRNG에서 와야 한다”는 접근이 마음에 듦
성능을 조금 잃는 대신 잘못된 난수 생성기를 써서 재앙을 일으키지 않는다는 훨씬 강한 보장을 얻는 쪽임
거의 모든 언어에서 개발자가 아직도 이 날카로운 모서리를 신경 써야 한다는 게 아쉬움
- PHP 8.2의 새 객체지향 난수 API에서는 정확히 그렇게 했음
-
모르는 사람을 위해 덧붙이면,
gosec와 그 확장인golangci-lint는math/rand사용을 경고해 줌
https://github.com/securego/gosec/blob/d3b2359ae29fe344f4df5...math/rand/v2에서 가장 마음에 드는 점 중 하나는 회사에서nolint지시어와 그 뒤의 PR 토론 없이 쓸 수 있다는 것임
-
보안과 새 v2 옵션에 대한 권고를 아직 해석 중임
블로그 글은 “비밀값에는 다른 것이 필요하다” 같은 문장을 쓰고, 이어서 암호학적 난수성, ChaCha8, 시스템 난수로 시드되는 방식까지 자세히 다뤄서 매우 “안전”하다는 인상을 줌
그런데 패키지 문서에는 이렇게 되어 있음
... but it should not be used for security-sensitive work ... This package's outputs might be easily predictable regardless of how it's seeded. For random numbers suitable for security-sensitive work, see the crypto/rand package.
그렇다면 왜 블로그 글에서math/rand/v2를 “비밀값”에 쓰는 듯한 암시를 주는 걸까?
짧게 말하면 민감한 것은 여전히 모두crypto/rand를 써야 하고, 여기 설명된 개선은 누군가math/rand/v2를 부적절하게 썼을 때의 안전망이라는 뜻인가?- 맞음.
math/rand/v2가 최적은 아니지만, 더 이상crypto/rand를 써야 할 곳에 실수로 썼다고 즉시 치명적인 보안 결함이 되지는 않음
글에도 이렇게 나옴
crypto/rand를 쓰는 편이 여전히 더 낫다. 운영체제 커널은 여러 엿보기 공격으로부터 난수 값을 비밀로 유지하는 일을 더 잘할 수 있고, 커널은 생성기에 계속 새 엔트로피를 더하며, 더 많은 검토를 받아왔기 때문임. 하지만 실수로math/rand를 쓰는 일이 더 이상 보안 재앙은 아님
- 맞음.
-
최악의 벤치마크에서도 새 전략은 안전하지 않은 난수 생성기보다 대략 절반 정도 느릴 뿐이고, 대부분의 벤치마크는 훨씬 더 가까웠음
Go는 표준 라이브러리와 그 위에 만들어지는 앱을 위해 안전성과 성능의 균형을 잘 잡고 있음. 다른 생태계도 따라오면 좋겠음
애플리케이션에 빠르고 안전하지 않은 난수가 필요하다면 내부 생성기를 직접 구현해야 함
손쉽게 닿는 곳에 안전하지 않은 난수를 두는 것은 치워둘 수 있는 발등 찍는 도구임- 솔직히 이건 더 나빠 보임
사람들이"random"원시 기능이 암호학적으로 안전하다고 가정하도록 부추기는 건 나쁜 관행을 장려하는 것임
math/rand/v2를 암호학적으로 안전하게 만드는 것이 한 문제를 해결할 수는 있지만, 이제 보안을 약속하는 것처럼 보이지 않는 것이 “괜찮은” 상태가 됨
일반적으로math/rand함수에는 암호학적으로 안전하다는 관례가 없음. 그것을 바꿔서 나쁜 코드가 우연히 올바르게 동작하게 만들면, 우리가 이런 명백한 실수를 하고 있다면 다른 어떤 실수도 하고 있는지 가려질 수 있음
- 솔직히 이건 더 나빠 보임
-
Go 1의
math/rand는 더 정확히는 가산 지연 피보나치 생성기라고 부르는 편이 맞음
최초 발표는 Green, Smith, Klem의 논문임
[1] https://doi.org/10.1145/320998.321006- 그 논문에는 “지연” 부분이 언급되지 않는 것 같음. 아니면 내가 놓쳤을 수도 있음
https://www.leviathansecurity.com/blog/attacking-gos-lagged-...도 알고 있고, 여기서도 지연 피보나치 생성기라고 부름
Rob Pike와 나는 몇 달 전 Go 1 생성기의 원래 C 버전을 작성한 Don Mitchell과 메일을 주고받으며 그가 이 알고리즘을 어떻게 설명할지 물었고, 그는 “기억하기로 Jim과 나는 Marsaglia의 LFSR 비슷한 생성기를 구현했다”고 답했음
두 설명, 즉 지연 피보나치와 LFSR 유사 생성기는 서로 다른 관점에서 모두 정확하다고 봄. 어느 쪽도 괜찮지만, 글에서는 원저자의 설명을 쓰기로 했음
- 그 논문에는 “지연” 부분이 언급되지 않는 것 같음. 아니면 내가 놓쳤을 수도 있음
-
작은 흠을 하나 꼽자면, 여기서는 통계적 무작위성과 의사 난수 생성기가 섞여 쓰인 것 같음
위키의 통계적 무작위성 정의는 “숫자열에 알아볼 수 있는 패턴이나 규칙성이 없을 때 통계적으로 무작위라고 한다”임
이 정의가 진정한 난수 생성기(TRNG)에도 적용될까? 적용되기를 바라야 함. 적어도 장기적으로나 극한에서는 그래야 함. 그렇지 않다면 TRNG가 아님
TRNG는 장기적으로 “알아볼 수 있는 패턴이나 규칙성이 없는 숫자열”을 생성해야 함
따라서 통계적 무작위성이 PRNG를 뜻하지는 않지만, TRNG에도 적용될 수 있다고 말할 수 있음
문제는 PRNG가 제한된 형태의 통계적 무작위성을 갖는지 검증하기 위한 통계적 무작위성 테스트가 많이 있다는 데서 오는 듯함
그래서 PRNG를 식별하려면 “통계적 무작위성”보다 “의사 난수 생성기”라는 표현이 더 적절했을 것 같음. 그래도 아주 작은 흠임