Trong kỷ nguyên số, nơi mọi hoạt động đều được số hóa, việc tạo ra các con số ngẫu nhiên an toàn là một trong những nền tảng quan trọng nhất của bảo mật. Tuy nhiên, bản chất của máy tính là deterministic (có tính xác định), điều này khiến cho việc tạo ra “ngẫu nhiên” thực sự trở thành một thách thức lớn. Mọi thứ được coi là “ngẫu nhiên” trên máy tính đều được dẫn xuất từ các giá trị “thực” thông qua một thuật toán. Do đó, tính bảo mật của những con số này phụ thuộc hoàn toàn vào việc gieo hạt (seeding) chúng bằng dữ liệu mà kẻ tấn công không thể đoán trước được. Nếu có thể dự đoán dữ liệu gốc mà máy tính sử dụng, thì tính ngẫu nhiên sẽ không còn nữa. Các hệ điều hành hiện đại đã tích hợp các bộ thu thập entropy (độ ngẫu nhiên) được kiểm định kỹ lưỡng và các bộ tạo số giả ngẫu nhiên an toàn về mặt mật mã (CSPRNGs – Cryptographically Secure Pseudorandom Number Generators), và đối với hầu hết các trường hợp sử dụng, chúng là đủ.
Tuy nhiên, việc bổ sung thêm dữ liệu không thể đoán trước từ thế giới vật lý là một cách để nâng cao tính bảo mật lên một tầm cao mới. Đây chính là lý do bạn có thể đã nghe nói về các giải pháp độc đáo như bức tường đèn dung nham của Cloudflare (Cloudflare’s wall of lava lamps), mà công ty này chụp ảnh liên tục để tạo ra một luồng byte có entropy cao, phục vụ cho việc tạo khóa TLS. Lý do đằng sau giải pháp này rất đơn giản: các cảnh quan thiên nhiên động tạo ra dữ liệu pixel biến đổi cao, ngay cả nhiễu cảm biến cấp thấp cũng thêm vào tính không thể đoán trước. Các ảnh chụp định kỳ có thể được hash và trộn vào một pool entropy để tăng cường tính ngẫu nhiên. Lấy cảm hứng từ ý tưởng đó, chúng tôi đã xây dựng một giải pháp thay thế của riêng mình bằng cách sử dụng Frigate, một hệ thống NVR mã nguồn mở, và một chiếc camera Wi-Fi giá rẻ.
Để thực hiện điều này một cách đúng đắn, bạn cần phải lấy dữ liệu hình ảnh thô từ camera và có một nguồn entropy ngay trước nó. Tôi sống ở Ireland, một đất nước rất nhiều gió, và camera CCTV ngoài trời của tôi hướng vào một nhóm cây cối đang lung lay và di chuyển rất nhiều, đó chính là nguồn entropy của tôi. Nếu bạn có một camera được kết nối với máy tính Linux, bạn có thể lấy dữ liệu thô bằng một công cụ như fswebcam
. Dự án này chỉ là một proof of concept (bằng chứng về khái niệm) hơn là một giải pháp bảo mật thực sự, và nên được coi như vậy. Đây không phải là một giải pháp thay thế trực tiếp cho RNG của hệ điều hành của bạn.
Tầm Quan Trọng Của Entropy Và Vai Trò Của Frigate
Tính Ngẫu Nhiên Trong Mật Mã Học Là Gì?
Trong ngôn ngữ đời thường, entropy là một thước đo của sự hỗn loạn, mất trật tự. Nhưng trong mật mã học, entropy được hiểu tốt hơn là tính không thể đoán trước. Nguồn ngẫu nhiên của bạn càng có nhiều entropy, kẻ tấn công càng khó phát hiện ra các mẫu, và các khóa, IVs (Initialisation Vectors), nonces (numbers used once), và one-time pads của bạn càng mạnh mẽ. Hầu hết các hệ điều hành đã duy trì một kernel entropy pool (bộ nhớ chứa entropy trong nhân hệ điều hành), và bạn thậm chí có thể thấy điều này trên Linux bằng cách chạy lệnh sau:
head -c 32 /dev/urandom | hexdump -C
Lưu ý rằng /dev/urandom, khi được đọc trong thời gian khởi động sớm, có thể trả về dữ liệu trước khi pool entropy của hệ thống được khởi tạo. Kết quả là, getRandom
hoặc /dev/random
có thể được sử dụng thay thế, mặc dù /dev/urandom vẫn là lựa chọn tốt hơn cho hầu hết các trường hợp.
Như đã nói, đôi khi bạn cần tính ngẫu nhiên cụ thể cho một ứng dụng. Một ví dụ điển hình là ví cứng (hardware wallet) luôn duy trì tính xác định qua các chu kỳ nguồn. Hơn nữa, dữ liệu thế giới thực luôn vượt trội hơn bất kỳ thứ gì hệ thống của bạn tạo ra dưới dạng entropy, và đó là lúc một bộ cấp entropy chuyên dụng có thể hữu ích. Đã có những ví dụ khác về bộ tạo entropy mà bạn có thể sử dụng, như tính ngẫu nhiên thực sự của random.org dựa trên nhiễu khí quyển.
Nếu bạn đã từng nghe về khái niệm thao túng RNG (RNG manipulation), phổ biến trong nhiều trò chơi và thường là một thành phần của các nỗ lực speedrun, thì cùng một khái niệm này cũng áp dụng cho mọi cấp độ ngẫu nhiên trên máy tính. Mặc dù thao túng RNG thường vô hại trong một trò chơi, nhưng điều đó không còn đúng khi đối phó với bảo mật thực tế. Nói như vậy, các trò chơi (đặc biệt là các tựa game cũ) sẽ sử dụng các thuật toán đơn giản hơn, dễ đoán hơn cho các sự kiện “ngẫu nhiên” của chúng, và thao túng RNG là một chiến thuật phổ biến trong các trò chơi như Pokémon để bắt Pokémon shiny hoặc gặp Pokémon có chỉ số tốt hơn.
Những gì chúng tôi đã tạo ra, về cơ bản, là một giải pháp trung gian giữa một thứ quá phức tạp như bức tường đèn dung nham của Cloudflare và việc không có bất kỳ entropy nào cả. Chúng tôi đã có Frigate được cấu hình với một vài camera, vì vậy việc sử dụng nó và API của nó để tạo khóa riêng khi cần là khá dễ dàng. Hầu hết mọi người không bao giờ cần tạo khóa mật mã an toàn ở cấp độ này, nhưng đây là một bài tập để chứng minh cách nó có thể được thực hiện và sử dụng nó như một công cụ để giải thích tại sao nó cần thiết trong một số ngữ cảnh.
Bức tường đèn dung nham của Cloudflare tạo entropy cho bảo mật dữ liệu
Vì Sao Chọn Frigate?
Lý do chúng tôi sử dụng Frigate là vì nó có một API tuyệt vời mà bất kỳ ai có quyền ủy quyền đều có thể truy cập. Nó dễ sử dụng, là một phần mềm được triển khai phổ biến, và tính năng API quan trọng nhất cho dự án này là khả năng lấy một snapshot (ảnh chụp nhanh) của khung hình mới nhất được camera chỉ định. Nó nằm tại địa chỉ:
https://(IP):5000/api/(camera_name)/latest.jpg
Điểm hay của việc này là mỗi khung hình sẽ khác nhau, ngay cả khi, bằng mắt thường, hình ảnh không thay đổi. Ánh sáng, chuyển động và các khía cạnh khác của bức ảnh khiến dữ liệu có thể sẽ không bao giờ thực sự giống nhau, và điều đó quan trọng vì lý do entropy. Một luồng byte đầu vào không bao giờ nên giống hệt một luồng khác, vì vậy chúng ta có thể kết hợp điều đó với entropy được tính toán của hệ thống thông qua hàm dẫn xuất khóa (key derivation function) và xây dựng một “seed” duy nhất cho việc tạo mật mã của chúng ta. Tuy nhiên, mặc dù chúng tôi sử dụng JPG cho dự án này, định dạng JPG không được khuyến nghị cho một triển khai thực sự sẵn sàng cho sản xuất. Nó có một khối dữ liệu có thể dự đoán được và khá giống với phần header, và việc nén cũng sẽ tạo ra các khu vực của hình ảnh rất giống với cùng khu vực trong các hình ảnh khác. Bạn nên sử dụng các khung hình thô (raw frames) hoặc, ít nhất là các định dạng không nén như PNG và hash dữ liệu pixel không nén.
Do đó, dự án này là một proof of concept và nên được coi như vậy.
Xây Dựng Hệ Thống Tạo Entropy Từ Camera An Ninh
Thu Thập Ảnh Từ Frigate Và Tính Toán Hash
Để xây dựng dự án này một cách hoàn chỉnh, bạn sẽ cần một instance Frigate với một camera được kết nối, và bạn sẽ cần cài đặt Python với các thư viện cryptography
và requests
. Chúng ta sẽ tạo một hàm Python để thực hiện yêu cầu tới nguồn hình ảnh của chúng ta và sau đó trả về nó dưới dạng một luồng byte. Luồng dữ liệu này sau đó có thể được hash với việc tạo số giả ngẫu nhiên của hệ thống của chúng ta hoặc được sử dụng riêng để tạo seed. Dưới đây là đoạn mã Python dẫn xuất các giá trị đó:
# Build 512-bit seed from frame and 256 bits OS entropy
seed = hashlib.sha512(frame + os.urandom(32)).digest()
# Fresh HKDF object each iteration
aes_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=None,
info=b"frigate-rng",
backend=default_backend(),
).derive(seed)
# HMAC-SHA256 demo
h = hmac.HMAC(aes_key, hashes.SHA256(), backend=default_backend())
h.update(b"hello xda")
tag = h.finalize()
# Printable 64-char token
token = token_from_seed(seed)
def token_from_seed(seed: bytes, length: int = 64, symbols: bool = False) -> str:
alphabet = string.ascii_letters + string.digits
if symbols:
alphabet += string.punctuation
# HKDF just to expand/compress the seed into exactly <length> bytes
token_raw = HKDF(
algorithm=hashes.SHA256(),
length=length,
salt=None,
info=b"frigate-token",
backend=default_backend(),
).derive(seed)
return ''.join(alphabet[b % len(alphabet)] for b in token_raw)
Tại thời điểm này, chúng ta có mọi thứ cần thiết: một ảnh chụp nhanh từ camera thay đổi mỗi giây và một chút entropy của hệ thống. Chúng ta kết hợp hai luồng byte đó thành một seed 512-bit duy nhất và ngay lập tức dẫn xuất ba thành phần:
- Khóa AES-256: dài 32 byte, phù hợp cho các lược đồ như AES-GCM hoặc AES-SIV.
- Tag HMAC-SHA256: Xác thực thông điệp với một khóa mới.
- Token dài 64 ký tự: Bao gồm các ký tự chữ cái viết hoa, viết thường và số, có thể được sử dụng cho mật khẩu hoặc các ứng dụng yêu cầu token để lưu trữ cục bộ.
Sơ đồ hệ thống thu thập seed từ hình ảnh Frigate để tạo khóa mật mã an toàn
Kiểm Tra Tính Ngẫu Nhiên Với Dieharder
Tuy nhiên, chỉ vì nó nên ngẫu nhiên không có nghĩa là nó thực sự như vậy, vì vậy chúng tôi đã đưa thuật toán vào thử nghiệm. Chúng tôi đã gửi hàng nghìn yêu cầu tới máy chủ Frigate của mình, kiểm tra xem một hình ảnh có phải là duy nhất hay không. Nếu có, chúng tôi sẽ tải nó xuống, hash nó với khóa giả ngẫu nhiên được tạo bởi hệ thống, và sau đó lưu giá trị seed đầu ra vào một tệp. Một giải pháp thích hợp hơn cho việc này sẽ là chủ động hash từng hình ảnh và kiểm tra sự trùng lặp (collisions), nhưng đối với việc sử dụng này, nó hoạt động khá tốt. Chúng tôi đã sử dụng hai camera của mình trong Frigate để tăng tốc độ. Bằng cách thực hiện điều này lặp đi lặp lại, chúng ta có thể tạo ra rất nhiều kết quả khác nhau, và sau đó kiểm tra sự nối kết cuối cùng của mọi seed bằng một công cụ như Dieharder.
Dieharder là một công cụ miễn phí được xây dựng để kiểm tra các bộ tạo số ngẫu nhiên, nhưng ý tưởng là nó được cho là nhận gigabyte dữ liệu và xác định xem có bất kỳ mẫu nào hay không. Khi bạn cung cấp cho nó một kích thước mẫu giới hạn, nó sẽ “rewind” tệp một số lần, và điều này sẽ ảnh hưởng đáng kể đến kết quả kiểm tra. Chúng tôi đã không cung cấp cho nó gần đủ lượng dữ liệu mà nó thường