2 điểm bởi GN⁺ 2024-05-08 | 1 bình luận | Chia sẻ qua WhatsApp
  • Go 1.22 thay nguồn số ngẫu nhiên mặc định của math/randmath/rand/v2 bằ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ùng crypto/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/v2 cả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ủa math/rand và 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/randmath/rand/v2 sang 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ần crypto/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
  • srandrand củ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/rand củ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 vec gồm 607 uint64
    • vec[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ưu x và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 vec và 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ứ
  • 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 vec ban đầu

PCG cải thiện điều gì và giới hạn còn lại

  • math/rand/v2 dù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 scramble dự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
  • 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/rand củ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/v2 luôn dùng ChaCha8Rand
    • Ví dụ: rand.N, rand.Float64
  • Các hàm package trong math/rand dùng ChaCha8Rand nếu chưa gọi rand.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
  • 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 Read của math/rand bị deprecated trong Go 1.20, một số lập trình viên mới phát hiện ra họ đã dùng math/rand ở nơi cần crypto/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/rand là đủ vì UUID không phải giá trị bí mật
  • Nhưng nếu seed math/rand bằ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ề uint64 tiếp theo từ luồng ngẫu nhiên
    • N(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: Uint64 nhanh hơn PCG: Uint64, nhưng Go 1: N(1000) lại chậm hơn PCG: N(1000)
    • N(1000) của Go 1 dùng hai phép chia số nguyên 64 bit để thu hẹp phạm vi
    • N(1000) của PCG và ChaCha8 chủ yếu dùng thuật toán nhanh hơn của math/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 cho crypto/rand
  • Vẫn có những trường hợp như gói npm keypair cố tạo cặp khóa RSA bằng JavaScript Math.random khi 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

 
GN⁺ 2024-05-08
Ý 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 Read của crypto/rand, import đã tự động bị đổi, có lẽ do bị trộn với mã dùng math/rand nên goimports đã đổi sang math/rand
    Kế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

    • Nghe đau thật, xin lỗi. Vì năm 2016 goimports đã được đổi để ưu tiên crypto/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/rand
      https://go-review.googlesource.com/24847
      Dù sao cũng mừng là phần này đã được dọn dẹp
    • Tôi cũng từng nhận được báo cáo lỗ hổng do ai đó nhầm rằng 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ào
      text/templatehtml/template cũ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ệ
    • Tôi cũng từng thấy khi tìm "secure password generation golang" thì gần như mọi ví dụ đều dùng math/rand
      Tệ 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ừ đâu
    • goimports gần như ngay từ đầu đã xử lý đặc biệt math/rand.Readcrypto/rand.Read
      Tuy 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 đó
    • Có vẻ cũng không quá khó để cung cấp một lời gọi API có tên kiểu "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

    • Nhờ loạt bài của ông ấy về automata hữu hạn và biểu thức chính quy mà tôi đã mê lĩnh vực đó
      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
    • Ông ấy cũng làm demo video rất tốt https://research.swtch.com/acme
  • Tôi từng dùng nhầm math/rand ở chỗ lẽ ra bắt buộc phải dùng crypto/rand
    Kế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 import math/rand vào đúng file đó
    Từ đó về sau, để tránh tự động import nhầm rand, tôi import crypto/rand với alias cryptorand
    Nhâ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

    • Tôi không biết chính xác trường hợp của bạn đã xảy ra chuyện gì, nhưng có khả năng nó không giống như bạn mô tả
      Năm 2016, goimports đã được đổi để ưu tiên crypto/rand hơn math/rand (https://go-review.googlesource.com/24847), và khi đó hỗ trợ VSCode cho Go còn chưa xuất hiện
    • Người khác cũng nói điều tương tự. Thành thật mà nói, thông lệ tự động thêm import trông hoàn toàn kỳ quặc, và phá hỏng chính mục đích của việc tách tên vào các không gian tên khác nhau
  • Ngay 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

    • API số ngẫu nhiên hướng đối tượng mới của PHP 8.2 đã làm đúng như vậy
      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 instance Mt19937 toàn cục, sang random_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
    • Gần đây tôi bắt đầu dùng một thư viện Go mới để tạo ID ngẫu nhiên cho nhiều thành phần của một cấu trúc dữ liệu phức tạp
      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ủa crypto/rand; trên MacBook của tôi, nó đang thực hiện system call
      Sau khi patch thư viện để dùng Read() của math/rand, hiệu năng cải thiện đáng kể
      Ngoài việc math/rand nhanh 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ậy
      Tô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/rand trên thực tế gần như không phân biệt được với math/rand nữa thì cũng tốt thôi :-)
    • Một trong những lập luận tốt hơn cho việc dùng CSPRNG, ở đây là ChaCha8, là trong benchmark nó chỉ chậm hơn PCG trong phạm vi dưới 2 lần
      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 mt19937 hay PRNG cũ của Go
      Nế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òn trường hợp nào khác cần loại trước? Tôi chỉ nghĩ đến seed cố định khi cần kết quả có thể tái lập, chẳng hạn như kiểm thử hoặc xác minh
      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/rand thự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à dd cũng chạy ổn
      Tôi cũng tự hỏi liệu với đa số trường hợp sử dụng, API kiểu Must/panic có phù hợp làm mặc định hơn không
      Nhân tiện, tôi xem gói secrets củ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?
    • Tôi thích cách tiếp cận “mọi số ngẫu nhiên trong hệ thống nên đến từ CSPRNG trừ khi chủ động opt out”
      Đổ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: gosec và phần mở rộng của nó là golangci-lint sẽ cảnh báo khi dùng math/rand
    https://github.com/securego/gosec/blob/d3b2359ae29fe344f4df5...

    • Một trong những điểm tôi thích nhất ở math/rand/v2 là có thể dùng nó ở công ty mà không cần chỉ thị nolint và 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/v2 cho “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ùng math/rand/v2 không đúng chỗ không?

    • Đúng vậy. math/rand/v2 không phải là tối ưu, nhưng nếu lỡ dùng nhầm ở nơi đáng ra phải dùng crypto/rand thì nó không còn ngay lập tức trở thành lỗ hổng bảo mật chí mạng nữa
      Trong bài cũng có đoạn như sau
      Dùng crypto/rand vẫ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ùng math/rand không còn là một thảm họa bảo mật nữa
  • Ngay 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

    • Nói thật thì điều này trông còn tệ hơn
      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ấu
      Việc làm cho math/rand/v2 an 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/rand khô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ác
  • math/rand của Go 1 gọi chính xác hơn là trình tạo Fibonacci trễ cộng
    Công bố đầu tiên là bài báo của Green, Smith, Klem
    [1] https://doi.org/10.1145/320998.321006

    • Bài báo đó dường như không đề cập đến phần “trễ”. Hoặc có thể tôi đã bỏ sót
      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ê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ỏ