- Go 1.22 thay nguồn số ngẫu nhiên mặc định của
math/randvàmath/rand/v2bằng bộ sinh mạnh về mặt mật mã, giúp giảm đáng kể thiệt hại khi vô tình dùng nhầm ở những chỗ lẽ ra phải dùngcrypto/rand - Bộ sinh Go 1 cũ là một thanh ghi dịch phản hồi tuyến tính dùng trạng thái gồm 607
uint64, nên chỉ cần quan sát 607 đầu ra là có thể khôi phục các giá trị quá khứ và tương lai - PCG-DXSM của
math/rand/v2cải thiện chất lượng ngẫu nhiên thống kê và kích thước trạng thái, nhưng vẫn không bảo đảm được tính không thể đoán trước cần cho giá trị bí mật - ChaCha8Rand mới dùng seed 32 byte, rekey sau mỗi 16 block và trạng thái 300 byte cho mỗi lõi; được áp dụng cho
math/rand/v2, một phần củamath/randvà seed băm của map - Chi phí hiệu năng bị giới hạn: ChaCha8Rand chậm hơn bộ sinh Go 1 nhưng không quá 2 lần, và trên máy chủ thông thường chênh lệch không vượt quá 3ns nên lợi ích bảo mật lớn hơn với phần lớn chương trình
Go 1.22 thay đổi mặc định về số ngẫu nhiên
- Go 1.22 đổi mặc định trong
math/randvàmath/rand/v2sang dùng bộ sinh số giả ngẫu nhiên mạnh về mặt mật mã - Mục tiêu là giảm thiệt hại khi lập trình viên vô tình dùng
math/randở nơi cầncrypto/rand - API số ngẫu nhiên của Go từ trước đến nay thường được chia thành hai nhóm
math/rand: ngẫu nhiên thống kê dùng cho mô phỏng, lấy mẫu, phân tích số, thuật toán ngẫu nhiên phi mật mã, fuzzing, xáo trộn, exponential backoff, v.v.crypto/rand: ngẫu nhiên mật mã dùng cho khóa, token và các trường hợp cần tính không thể đoán trước
Vì sao ngẫu nhiên thống kê là chưa đủ
- Bộ sinh số ngẫu nhiên thống kê có thể đủ cho nhiều mục đích phi mật mã nếu vượt qua các kiểm thử thống kê cơ bản
- Nhưng nếu người quan sát biết thuật toán và thấy đủ nhiều đầu ra, họ thường có thể dự đoán chuỗi tiếp theo
srandvàrandcủa Unix V3 là dạng đầu tiên đã ảnh hưởng đến API số ngẫu nhiên của C và nhiều ngôn ngữ khác- Thiết lập trạng thái bằng một seed số nguyên duy nhất
- Tính giá trị tiếp theo bằng cơ chế bộ sinh đồng dư tuyến tính (LCG)
- Trạng thái nội bộ đơn giản nên chỉ cần một đầu ra cũng có thể dễ dàng tính được các giá trị tương lai
- Có thể chọn hằng số trong LCG để phát ra mọi giá trị khả dĩ đúng một lần trước khi lặp lại, nhưng bit thấp có nhược điểm là lặp theo chu kỳ ngắn
Cấu trúc và điểm yếu của bộ sinh Go 1
- Bộ sinh
math/randcủa Go 1 thuộc họ thanh ghi dịch phản hồi tuyến tính - Trạng thái nội bộ là slice
vecgồm 607uint64vec[606]là “tap”vec[334]là “feed”- Khi tạo giá trị tiếp theo, nó cộng tap và feed để tạo
x, lưuxvào vị trí feed rồi trả về
- Cài đặt thực tế không dịch chuyển toàn bộ slice mà chỉ lùi vị trí tap và feed để giảm chi phí
- Việc tạo giá trị tiếp theo cần hai phép trừ, hai phép cộng có điều kiện, hai lần load, một phép cộng và một lần store
- Vì giá trị trả về là một phần tử của vector trạng thái nội bộ, chỉ cần đọc 607 đầu ra là lộ toàn bộ trạng thái
- Có thể điền lại cùng
vecvà chạy thuật toán để dự đoán giá trị tương lai - Nếu chạy ngược thuật toán, cũng có thể khôi phục giá trị quá khứ
- Có thể điền lại cùng
- Bộ sinh Go 1 không предназначен cho mục đích bảo mật, và chất lượng số sinh ra cũng phụ thuộc vào cách khởi tạo
vecban đầu
PCG cải thiện điều gì và giới hạn còn lại
math/rand/v2dùng bộ sinh ngẫu nhiên thống kê hiện đại hơn là PCG của Melissa O’Neill- PCG trong Go dựa trên LCG 128 bit và dùng hàm
scrambleđể rút trạng thái 128 bit xuống đầu ra 64 bit - Trong quá trình thảo luận đề xuất, Go đã dùng kiểu
scrambledựa trên phép nhân theo gợi ý của O’Neill- Dạng này được gọi là PCG-DXSM
- Numpy cũng dùng biến thể PCG này
- PCG có trạng thái nhỏ hơn rất nhiều so với bộ sinh Go 1
- Bộ sinh Go 1: 607
uint64 - PCG: hai
uint64
- Bộ sinh Go 1: 607
- PCG ít nhạy với trạng thái khởi tạo hơn và vượt qua nhiều kiểm thử thống kê, nhưng không bảo đảm tính không thể đoán trước
- PCG-XSL-RR có thể đảo ngược
- Sẽ không có gì đáng ngạc nhiên nếu PCG-DXSM cũng có thể bị đảo ngược
- Để tạo giá trị bí mật, cần một bộ sinh khác chứ không phải PCG
Ngẫu nhiên mật mã và vai trò của hệ điều hành
- Ngẫu nhiên mật mã phải gần như không thể đoán được ngay cả với người quan sát biết cách tạo ra nó và đã xem nhiều đầu ra trước đó
- Giao thức mật mã, khóa bí mật, thương mại hiện đại và quyền riêng tư trực tuyến đều phụ thuộc vào ngẫu nhiên mật mã
- Việc cung cấp nguồn ngẫu nhiên thực tế do hệ điều hành đảm nhiệm
- Thu thập tính ngẫu nhiên từ các thiết bị vật lý như chuột, bàn phím, đĩa và thời gian mạng
- Gần đây còn tận dụng cả nhiễu điện do CPU đo trực tiếp
- Khi hệ điều hành thu đủ ngẫu nhiên, chẳng hạn tối thiểu 256 bit, nó dùng hàm băm hoặc thuật toán mật mã để tạo ra chuỗi ngẫu nhiên dài
- Trước đây thường dùng file thiết bị như
/dev/random, nhưng hiện nay hệ điều hành cung cấp syscall trực tiếp crypto/randcủa Go che giấu khác biệt giữa các hệ điều hành và cung cấp cùng một giao diện làrand.Read
Thiết kế ChaCha8Rand
- Bộ sinh mới ChaCha8Rand trong Go 1.22 là một biến thể được chỉnh nhẹ từ mã dòng ChaCha của Daniel J. Bernstein
- ChaCha được dùng rộng rãi ở dạng ChaCha20 trong TLS và SSH
- Too Much Crypto của Jean-Philippe Aumasson cho rằng dạng 8 vòng là ChaCha8 cũng an toàn, và ChaCha8 nhanh hơn khoảng 2,5 lần
- Để dùng ChaCha8 như
rand.Source, ChaCha8Rand không XOR block sinh ra với đầu vào mà dùng trực tiếp làm luồng ngẫu nhiên- Điều này tương đương với mã hóa hoặc giải mã dữ liệu toàn số 0
Những thay đổi trong ChaCha8Rand
- ChaCha8Rand dùng seed 32 byte làm khóa ChaCha8
- ChaCha8 tạo ra block 64 byte, và quá trình tính toán xem block như 16
uint32 - Cài đặt thông thường có thể dùng lệnh SIMD để tính đồng thời 4 block, nhưng khi dùng cho đầu vào XOR thì phải tháo lại các block xen kẽ
- ChaCha8Rand định nghĩa chính block xen kẽ đó là luồng ngẫu nhiên để bỏ chi phí unshuffle
- Ở bước hoàn tất block của ChaCha8, một số giá trị nhất định được cộng vào từng
uint32- Một nửa là vật liệu khóa và một nửa là hằng số đã biết
- ChaCha8Rand không cộng lại các hằng số đã biết, nhờ đó bỏ được một nửa phép cộng ở bước cuối
- Ở mỗi block sinh ra thứ 16, 32 byte cuối cùng được dùng làm khóa cho 16 block tiếp theo
- Việc rekey này cung cấp một dạng an toàn tiến tới
- Ngay cả khi toàn bộ trạng thái bộ nhớ của bộ sinh bị lộ, cũng chỉ có thể khôi phục các giá trị từ lần rekey gần nhất chứ không truy được về quá khứ
- Go đã công bố đặc tả ChaCha8Rand C2SP và bộ test case để các cài đặt khác với cùng seed cũng có thể tái lập đúng như cài đặt của Go
Nơi được áp dụng trong thư viện chuẩn
- Runtime của Go duy trì trạng thái ChaCha8Rand theo từng lõi được seed bằng ngẫu nhiên mật mã do hệ điều hành cung cấp
- Kích thước trạng thái là 300 byte cho mỗi lõi
- Trên hệ thống 16 lõi, mức này tương đương với trạng thái 4.872 byte của bộ sinh Go 1 dùng chung duy nhất
- Trạng thái theo lõi cho phép tạo số ngẫu nhiên nhanh mà không bị tranh chấp lock
- Các hàm package trong
math/rand/v2luôn dùng ChaCha8Rand- Ví dụ:
rand.N,rand.Float64
- Ví dụ:
- Các hàm package trong
math/randdùng ChaCha8Rand nếu chưa gọirand.Seed- Ví dụ:
rand.Intn,rand.Float64 - Nếu gọi
rand.Seed, nó phải quay lại bộ sinh Go 1 để giữ tương thích
- Ví dụ:
- Runtime chọn seed băm cho map mới bằng ChaCha8Rand thay vì bộ sinh cũ dựa trên wyrand
- Nếu kẻ tấn công biết hàm băm cụ thể trong cài đặt map, họ có thể chuẩn bị đầu vào để đẩy map vào hành vi thời gian bậc hai
- Dùng seed riêng cho từng map thay vì một seed toàn cục còn giúp tránh các dạng suy biến khác
- Việc seed map có thật sự cần ngẫu nhiên mật mã hay không chưa hoàn toàn rõ, nhưng đây là một lựa chọn đơn giản và thận trọng
- Mã cần instance ChaCha8Rand riêng có thể tạo trực tiếp
rand.ChaCha8
Giảm thiểu thiệt hại do sai sót bảo mật
- Go hướng tới việc giảm hoặc loại bỏ các lỗi phổ biến có thể gây vấn đề bảo mật, để giúp viết mã an toàn theo mặc định
- Khi
Readcủamath/randbị deprecated trong Go 1.20, một số lập trình viên mới phát hiện ra họ đã dùngmath/randở nơi cầncrypto/rand, chẳng hạn tạo vật liệu khóa - Trong Go 1.20, kiểu nhầm lẫn này là vấn đề bảo mật nghiêm trọng
- Khóa được dùng ở đâu
- Khóa bị lộ như thế nào
- Các đầu ra ngẫu nhiên khác có cung cấp manh mối để kẻ tấn công suy ra khóa hay không
- Trong Go 1.22, cùng một sai sót đó vẫn là sai, nhưng khả năng biến thành thảm họa bảo mật đã giảm đi
- Dù vậy, với giá trị bí mật thì vẫn nên dùng
crypto/rand- Kernel của hệ điều hành có thể bảo vệ giá trị ngẫu nhiên tốt hơn
- Kernel tiếp tục bổ sung entropy mới vào bộ sinh
- Cài đặt trong kernel đã được xem xét nhiều hơn
Những trường hợp trông không giống mật mã
- Tạo UUID ngẫu nhiên có thể khiến người ta nghĩ
math/randlà đủ vì UUID không phải giá trị bí mật - Nhưng nếu seed
math/randbằng thời gian hiện tại, các máy khác nhau chạy cùng thời điểm có thể tạo ra cùng một giá trị- Trên các hệ thống chỉ cung cấp thời gian với độ chính xác mili giây, khả năng này còn cao hơn
- Dù Go 1.20 đã có auto-seeding dựa trên entropy của hệ điều hành, seed của bộ sinh Go 1 vẫn chỉ là số nguyên 63 bit
- Chương trình tạo UUID ngay lúc khởi động sẽ chỉ có 2⁶³ khả năng cho UUID đầu tiên
- Sau khoảng 2³¹ UUID, nguy cơ va chạm bắt đầu xuất hiện
- ChaCha8Rand của Go 1.22 được seed bằng entropy 256 bit
- Số UUID đầu tiên khả dĩ là 2²⁵⁶
- Hầu như không cần lo va chạm
- Cân bằng tải khi máy chủ frontend phân phối ngẫu nhiên request tới backend cũng có thể cần số ngẫu nhiên không thể đoán trước
- Nếu kẻ tấn công quan sát được cách phân phối và biết thuật toán có thể đoán trước, họ có thể dồn các request tốn kém vào một backend cụ thể
- Đây là vấn đề hiếm nhưng có thể xảy ra với bộ sinh Go 1
- Trong Go 1.22, nó không còn là vấn đề
Đặc tính hiệu năng
- Lợi ích bảo mật của ChaCha8Rand có đi kèm một chi phí nhỏ, nhưng hiệu năng vẫn cùng nhóm với bộ sinh Go 1 và PCG
- Có hai phép toán được đem ra so sánh
Uint64: trả vềuint64tiếp theo từ luồng ngẫu nhiênN(1000): trả về số ngẫu nhiên trong khoảng[0, 1000)
- Trên chip x86 64 bit nhưng build với
GOARCH=386để chạy ở chế độ 32 bit, PCG chậm hơn ChaCha8Rand vì phép nhân 128 bit của PCG- ChaCha8Rand dùng số học SIMD 32 bit
- Trên một số hệ thống,
Go 1: Uint64nhanh hơnPCG: Uint64, nhưngGo 1: N(1000)lại chậm hơnPCG: N(1000)N(1000)của Go 1 dùng hai phép chia số nguyên 64 bit để thu hẹp phạm viN(1000)của PCG và ChaCha8 chủ yếu dùng thuật toán nhanh hơn củamath/rand/v2để tránh phép chia
- Nhìn chung, ChaCha8Rand chậm hơn bộ sinh Go 1 nhưng không chậm quá 2 lần
- Trên máy chủ thông thường, chênh lệch không vượt quá 3ns, và rất ít chương trình coi đây là nút thắt cổ chai
Kết luận
- Go 1.22 nâng cao bảo mật của chương trình mà không cần sửa mã
- Cách tiếp cận cốt lõi là tăng cường chính
math/randđể giảm lỗi phổ biến khi vô tình dùng nó thay chocrypto/rand - Vẫn có những trường hợp như gói npm
keypaircố tạo cặp khóa RSA bằng JavaScriptMath.randomkhi không có Web Crypto API - Bảo mật hệ thống không thể phụ thuộc vào giả định rằng lập trình viên sẽ không mắc lỗi
- ChaCha8Rand của Go 1.22 cho thấy việc dùng bộ sinh số giả ngẫu nhiên mạnh về mặt mật mã ngay cả cho ngẫu nhiên “toán học” vẫn có thể đạt hiệu năng cạnh tranh với các bộ sinh khác
1 bình luận
Ý kiến trên Hacker News
Đúng như nội dung trong bài, rclone đã mắc chính xác lỗi này
Khi refactor đoạn mã dùng
Readcủacrypto/rand, import đã tự động bị đổi, có lẽ do bị trộn với mã dùngmath/randnêngoimportsđã đổi sangmath/randKết quả là nó không dùng bộ sinh số ngẫu nhiên an toàn, mà dùng bộ sinh quyết định được rclone seed bằng thời gian, và tôi đã không nhận ra trong diff :-(
https://www.cvedetails.com/cve/CVE-2020-28924/
Vì vậy tôi rất ủng hộ thay đổi lần này
goimportsđã được đổi để ưu tiêncrypto/rand, nên không chắc trong lúc refactor đã xảy ra chuyện gìCó thể trong cùng một file đã có mã dùng API riêng của
math/randhttps://go-review.googlesource.com/24847
Dù sao cũng mừng là phần này đã được dọn dẹp
math/randđang được dùng. Thực tế thì không phải, chỉ là họ nhầm lẫn giữa nhiều file nên không có vấn đề lớn, nhưng toàn bộ chuyện này cho thấy nó dễ gây rối đến mức nàotext/templatevàhtml/templatecũng tương tự. Nhìn lại thì kiểu che khuất tên package như vậy là một ý tưởng tệ"secure password generation golang"thì gần như mọi ví dụ đều dùngmath/randTệ hơn nữa là tất cả đều khởi tạo seed bằng thời gian hiện tại ngay trước khi tạo mật khẩu
Tôi biết chuyện này sau khi phát hiện có người trong code của chúng tôi dùng
math/rand, rồi đi tìm xem họ đã sao chép từ đâugoimportsgần như ngay từ đầu đã xử lý đặc biệtmath/rand.Readvàcrypto/rand.ReadTuy nhiên, trong commit năm 2016 https://github.com/golang/tools/commit/0835c735343e0d8e375f0... có nhắc đến một giai đoạn mà
"rand.Read"có thể được diễn giải là"math/rand"Có lẽ đã đúng vào giai đoạn đó
"PredictableRand"Tuần trước spacey cũng đã đăng ở https://news.ycombinator.com/item?id=40237491, nhưng bài đó có vẻ đã bị chôn nhầm vì bị xem là trùng với https://news.ycombinator.com/item?id=40224864
Hai bài blog trên go.dev là hai phần của cùng một loạt bài nhưng khá khác nhau. Bài lần này nói về thuật toán sinh số ngẫu nhiên an toàn hiệu quả, còn bài trước nói về thiết kế API của Go
Russell Cox luôn đều đặn cho ra những bài blog kỹ thuật, đề xuất và sản phẩm công việc rất xuất sắc
Nếu muốn nâng cao sự rõ ràng trong cách viết và tư duy, bắt đầu với Russell Cox là một điểm khởi đầu tốt
Khi đó tôi còn không biết Russ Cox là ai, nhưng loạt bài ấy thật sự rất tuyệt
Có lẽ đó là tài liệu miễn phí chất lượng nhất về triển khai biểu thức chính quy; sau đó là một số sách thiên về trình biên dịch, nhưng chúng không miễn phí và cũng khó tìm trên web
Tôi từng dùng nhầm
math/randở chỗ lẽ ra bắt buộc phải dùngcrypto/randKết quả là các phiên bản đầu của dnscrypt-proxy2 đã dùng khóa tĩnh
Nguyên nhân là tính năng của extension VSCode tự động thêm import. Trong mọi file nguồn cần số ngẫu nhiên an toàn, tôi đều cẩn thận tự import
crypto/rand, nhưng đã sót một file; mọi thứ vẫn biên dịch và chạy tốt, và tôi không nhận ra extension đã âm thầm thêm importmath/randvào đúng file đóTừ đó về sau, để tránh tự động import nhầm
rand, tôi importcrypto/randvới aliascryptorandNhân tiện, Zig cũng dùng bộ sinh số ngẫu nhiên dựa trên ChaCha8, và trong các thao tác mật mã, người dùng không thể cung cấp bộ sinh của riêng mình mà luôn dùng bộ sinh an toàn. Để phục vụ kiểm thử, một số hàm nhận seed tường minh
Với môi trường hạn chế, thư viện chuẩn cũng có một bộ sinh nhỏ hơn dựa trên hoán vị Ascon và cấu trúc Reverie
Năm 2016,
goimportsđã được đổi để ưu tiêncrypto/randhơnmath/rand(https://go-review.googlesource.com/24847), và khi đó hỗ trợ VSCode cho Go còn chưa xuất hiệnNgay cả trong thập niên 2020, tôi vẫn thường tự hỏi vì sao triển khai số ngẫu nhiên mặc định của nhiều ngôn ngữ lập trình lại dùng các bộ sinh số ngẫu nhiên nhanh như LFSR, MT
Có vẻ tốt hơn nếu giả định một cách thận trọng rằng mọi người không biết mình cần bộ sinh số giả ngẫu nhiên hay bộ sinh số giả ngẫu nhiên an toàn về mặt mật mã, rồi đổi mặc định sang loại sau và chỉ để những ai cần loại trước chủ động chọn rõ ràng
Nếu lập trình viên không chọn rõ ràng engine sinh số ngẫu nhiên sẽ dùng, họ sẽ nhận được bộ sinh an toàn về mặt mật mã
Giờ phần khó là thuyết phục mọi người chuyển sang API mới. Xa hơn nữa, ngay cả việc chuyển từ
mt_rand(), vốn dùng instanceMt19937toàn cục, sangrandom_int()dựa trên CSPRNG đã có từ PHP 7.0 cũng không hề dễ[1] https://www.php.net/releases/8.2/en.php#random_extension
Trường hợp sử dụng của tôi có hàng chục nghìn thành phần, và khi profiling thì thấy một phần đáng kể thời gian khởi tạo cấu trúc dữ liệu được dùng cho
Read()củacrypto/rand; trên MacBook của tôi, nó đang thực hiện system callSau khi patch thư viện để dùng
Read()củamath/rand, hiệu năng cải thiện đáng kểNgoài việc
math/randnhanh hơn, tôi còn lo rằng mình đang làm cạn entropy pool của hệ thống mà không có lý do gì đặc biệt. Trong trường hợp này, lý do duy nhất ID cần phải ngẫu nhiên là để sau khi serialize/deserialize cấu trúc dữ liệu, có thể thêm thành phần vào sau này, nhưng tôi không định làm vậyTôi không biết thời điểm thay đổi được nói trong blog này khớp chính xác thế nào với trải nghiệm của tôi. Có lẽ tôi đã dùng phiên bản thư viện cũ, và nếu giờ
crypto/randtrên thực tế gần như không phân biệt được vớimath/randnữa thì cũng tốt thôi :-)Kích thước trạng thái vẫn tương đối lớn (64 byte so với 16 byte), nhưng tốt hơn nhiều so với
mt19937hay PRNG cũ của GoNếu CSPRNG chậm hơn rất nhiều, như thường thấy với CSPRNG thông thường chứ không phải biến thể ChaCha giảm số vòng, thì sức hấp dẫn của nó với tư cách giá trị mặc định sẽ giảm đi
Có một yếu tố nhỏ nữa khiến mọi người bị đẩy về phía PRNG dù không cần seed. API CSPRNG luôn có lỗi phải xử lý để phòng trường hợp system call thất bại hoặc thiếu entropy
Việc đọc
crypto/randthực tế thường thất bại đến mức nào? Cần đọc nhiều đến đâu để làm cạn entropy trên hệ thống hiện đại? Tôi chưa từng thấy thất bại dù với hàng tỷ request, vàddcũng chạy ổnTôi cũng tự hỏi liệu với đa số trường hợp sử dụng, API kiểu
Must/paniccó phù hợp làm mặc định hơn khôngNhân tiện, tôi xem gói
secretscủa Python (https://docs.python.org/3/library/secrets.html) và không thấy nhắc gì đến việc nó có thể ném exception. Trong thực tế, chuyện đó đơn giản là không xảy ra sao?Đổi lại một chút hiệu năng, ta có được đảm bảo mạnh hơn nhiều rằng mình sẽ không gây thảm họa vì dùng sai bộ sinh số ngẫu nhiên
Thật tiếc là trong gần như mọi ngôn ngữ, lập trình viên vẫn phải để ý đến góc cạnh sắc nhọn này
Bổ sung cho những ai chưa biết:
gosecvà phần mở rộng của nó làgolangci-lintsẽ cảnh báo khi dùngmath/randhttps://github.com/securego/gosec/blob/d3b2359ae29fe344f4df5...
math/rand/v2là có thể dùng nó ở công ty mà không cần chỉ thịnolintvà cuộc thảo luận PR đi kèm sau đóTôi vẫn đang diễn giải các khuyến nghị về bảo mật và các tùy chọn v2 mới
Bài blog dùng những câu như “các giá trị bí mật cần thứ khác”, rồi sau đó đi vào chi tiết về tính ngẫu nhiên mật mã, ChaCha8 và cách nó được seed bằng số ngẫu nhiên hệ thống, tạo ấn tượng rất “an toàn”
Nhưng tài liệu package lại viết như sau
... 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.Vậy vì sao bài blog lại tạo hàm ý như thể dùng
math/rand/v2cho “giá trị bí mật”?Nói ngắn gọn, có phải mọi thứ nhạy cảm vẫn nên dùng
crypto/rand, còn cải tiến được mô tả ở đây là một lưới an toàn khi ai đó dùngmath/rand/v2không đúng chỗ không?math/rand/v2không phải là tối ưu, nhưng nếu lỡ dùng nhầm ở nơi đáng ra phải dùngcrypto/randthì nó không còn ngay lập tức trở thành lỗ hổng bảo mật chí mạng nữaTrong bài cũng có đoạn như sau
Dùng
crypto/randvẫn tốt hơn. Lý do là kernel của hệ điều hành có thể làm tốt hơn trong việc giữ bí mật các giá trị ngẫu nhiên trước nhiều kiểu tấn công rình xem, kernel liên tục bổ sung entropy mới vào bộ sinh, và nó đã được rà soát nhiều hơn. Nhưng việc lỡ dùngmath/randkhông còn là một thảm họa bảo mật nữaNgay cả trong benchmark tệ nhất, chiến lược mới cũng chỉ chậm hơn trình tạo số ngẫu nhiên không an toàn khoảng một nửa, và phần lớn các benchmark còn sát hơn nhiều
Go đang cân bằng tốt giữa độ an toàn và hiệu năng cho thư viện chuẩn và các ứng dụng được xây dựng trên đó. Sẽ tốt nếu các hệ sinh thái khác cũng theo kịp
Nếu ứng dụng cần số ngẫu nhiên nhanh và không an toàn, bạn nên tự triển khai trình tạo nội bộ
Đặt số ngẫu nhiên không an toàn ở nơi dễ với tới là một công cụ tự bắn vào chân có thể được cất đi
Khuyến khích mọi người giả định primitive
"random"là an toàn về mặt mật mã là cổ vũ cho thực hành xấuViệc làm cho
math/rand/v2an toàn về mặt mật mã có thể giải quyết một vấn đề, nhưng giờ đây những thứ trông không hề hứa hẹn về bảo mật lại trở thành trạng thái “ổn”Nói chung, các hàm
math/randkhông có quy ước là an toàn về mặt mật mã. Nếu thay đổi điều đó để mã xấu tình cờ hoạt động đúng, thì khi chúng ta mắc một lỗi hiển nhiên như vậy, nó có thể che khuất việc chúng ta còn đang mắc những lỗi nào khácmath/randcủa Go 1 gọi chính xác hơn là trình tạo Fibonacci trễ cộngCông bố đầu tiên là bài báo của Green, Smith, Klem
[1] https://doi.org/10.1145/320998.321006
Tôi cũng biết https://www.leviathansecurity.com/blog/attacking-gos-lagged-..., và ở đó cũng gọi nó là trình tạo Fibonacci trễ
Rob Pike và tôi vài tháng trước đã trao đổi email với Don Mitchell, người viết phiên bản C gốc của trình tạo Go 1, và hỏi ông sẽ mô tả thuật toán này như thế nào; ông trả lời: “Theo trí nhớ của tôi, Jim và tôi đã triển khai một trình tạo kiểu LFSR của Marsaglia”
Tôi cho rằng cả hai cách mô tả, tức Fibonacci trễ và trình tạo kiểu LFSR, đều đúng từ những góc nhìn khác nhau. Cách nào cũng được, nhưng trong bài viết tôi quyết định dùng mô tả của tác giả gốc
Nếu phải chỉ ra một điểm nhỏ, thì ở đây có vẻ tính ngẫu nhiên thống kê và trình tạo số giả ngẫu nhiên đang bị dùng lẫn với nhau
Định nghĩa về tính ngẫu nhiên thống kê trên wiki là “một chuỗi số được gọi là ngẫu nhiên về mặt thống kê khi nó không có mẫu hình hay tính quy luật có thể nhận biết được”
Định nghĩa này có áp dụng cho trình tạo số ngẫu nhiên thực sự (TRNG) không? Ta phải mong là có. Ít nhất là về dài hạn hoặc ở giới hạn thì phải như vậy. Nếu không thì nó không phải TRNG
Về dài hạn, TRNG phải tạo ra “chuỗi số không có mẫu hình hay tính quy luật có thể nhận biết được”
Vì vậy có thể nói tính ngẫu nhiên thống kê không có nghĩa là PRNG, nhưng cũng có thể áp dụng cho TRNG
Vấn đề dường như đến từ việc có nhiều kiểm thử tính ngẫu nhiên thống kê nhằm xác minh rằng PRNG có một dạng tính ngẫu nhiên thống kê bị giới hạn
Vì thế, để chỉ PRNG, có lẽ dùng cụm “trình tạo số giả ngẫu nhiên” sẽ phù hợp hơn “tính ngẫu nhiên thống kê”. Dù sao đây cũng chỉ là một điểm rất nhỏ