Câu 1Module nào bạn nên dùng để sinh một token đặt lại mật khẩu?
random và secrets — Chọn loại số ngẫu nhiên đúng
Học module random và secrets của Python từ căn bản. Bao quát chuỗi giả ngẫu nhiên có thể tái lập với random.choice / randint / shuffle / sample cùng seed, token an toàn về mặt mật mã với secrets.token_hex / token_urlsafe, và khi nào dùng cái nào — kèm bài tập thực hành chạy được.
Bài này bao quát hai module số ngẫu nhiên trong thư viện chuẩn. random cung cấp giá trị giả ngẫu nhiên cho test, game và mô phỏng (có thể tái lập với seed), và secrets cung cấp giá trị không thể đoán trước cho mật khẩu, token đặt lại và API key (lấy từ nguồn ngẫu nhiên mật mã của OS). Chọn đúng cái cho công việc là điều mà phần còn lại của bài bàn đến.
random và secrets khác nhau như thế nào
Cả hai module đều trả về số ngẫu nhiên, nhưng cơ chế và trường hợp sử dụng hoàn toàn khác nhau. random được xây dựng trên Mersenne Twister (một bộ sinh số giả ngẫu nhiên được sử dụng rộng rãi), và gọi random.seed(giá trị) cho phép bạn tái lập cùng một chuỗi. secrets, ngược lại, lấy từ nguồn ngẫu nhiên mật mã của OS (như /dev/urandom), nên không có khái niệm `seed` và các giá trị không thể đoán mỗi lần.
Với các trường hợp như test hay game — "không sao nếu kết quả bị lộ" — dùng random. Với mật khẩu, token và session ID — "nếu ai đó đoán được giá trị thì đó là sự cố bảo mật" — dùng secrets.
| Khía cạnh | random | secrets |
|---|---|---|
| Nguồn | Mersenne Twister (thuật toán) | Nguồn mật mã của OS (/dev/urandom, v.v.) |
| Khả năng tái lập | Cùng chuỗi tái lập được với seed | Không tái lập được (luôn khác) |
| Tốc độ | Nhanh | Chậm hơn (không phải vấn đề với mục đích sử dụng điển hình) |
| Phù hợp nhất | Test, game, mô phỏng | Token, mật khẩu, session ID |
random — Cơ bản (randint / uniform / choice)
Cơ bản của random là ba thứ: một số nguyên ngẫu nhiên / một float ngẫu nhiên / chọn một phần tử từ danh sách. random.randint(min, max) trả về một số nguyên bao gồm cả hai đầu, random.uniform(min, max) trả về một float trong khoảng đã cho, và random.choice(danh sách) trả về một phần tử từ danh sách.
Hãy bắt đầu mà không dùng seed và xem các hàm cơ bản này hoạt động ra sao.
| Hàm | Trả về | Sử dụng điển hình |
|---|---|---|
| random.randint(a, b) | Số nguyên trong [a, b] (cả hai đầu) | Tung xúc xắc, ID test |
| random.uniform(a, b) | Float trong [a, b] | Nhiễu, mô phỏng xác suất |
| random.choice(seq) | Một phần tử của chuỗi | Chọn một mục từ menu |
random — Tái lập bằng seed và thao tác trên collection
Bây giờ hãy xem khả năng tái lập test với `random.seed` và các hàm thao tác trên collection (shuffle / sample). Sau khi gọi random.seed(N), cùng một seed luôn tạo ra cùng một chuỗi. random.shuffle(danh sách) đảo các phần tử của danh sách tại chỗ, và random.sample(danh sách, k) trả về một danh sách mới gồm k phần tử duy nhất lấy từ nó.
| Hàm | Trả về | Sử dụng điển hình |
|---|---|---|
| random.seed(value) | None (khởi tạo trạng thái nội bộ) | Test có thể tái lập |
| random.shuffle(seq) | None (đảo danh sách gốc) | Ngẫu nhiên hóa thứ tự một danh sách |
| random.sample(seq, k) | Danh sách mới gồm k phần tử duy nhất | Bốc thăm k người từ khảo sát |
secrets — Ngẫu nhiên mạnh cho bảo mật
Module random ở các phần trước là một bộ sinh giả ngẫu nhiên cho test — nếu trạng thái nội bộ của nó bị lộ, các giá trị tiếp theo có thể bị đoán trước. Với những thứ như token đặt lại mật khẩu, API key và session ID — "nếu kẻ tấn công có thể đoán giá trị, bạn có lỗ hổng" — chọn module `secrets`, sử dụng nguồn ngẫu nhiên mật mã của OS.
secrets có API nhỏ hơn nhiều so với random — chủ yếu là các helper biến byte ngẫu nhiên thành chuỗi hex hoặc chuỗi an toàn cho URL.
| Hàm | Trả về | Sử dụng điển hình |
|---|---|---|
| secrets.token_bytes(n) | n byte ngẫu nhiên | Khóa mật mã, token nhị phân nội bộ |
| secrets.token_hex(n) | Chuỗi hex độ dài 2*n | Session ID đăng nhập |
| secrets.token_urlsafe(n) | Chuỗi an toàn URL (kiểu Base64) | Nhúng vào URL của link đặt lại |
| secrets.choice(seq) | Một phần tử từ seq, theo mật mã | Khi thứ tự hiển thị không nên đoán được |
| secrets.compare_digest(a, b) | True / False (chống tấn công thời gian) | So sánh hash / token (dùng thay cho ==) |
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2Sau khi gọi random.seed(42) rồi random.randint(1, 100), điều gì xảy ra nếu bạn làm lại y như vậy?
Câu 3Lựa chọn tốt nhất khi bạn muốn một danh sách được xáo trộn ngẫu nhiên có thể tái lập cho test?