Điều Gì Làm Cho Định Dạng Lưu Trữ Có Thể Khôi Phục
Bốn định dạng lưu trữ, xếp theo mức độ có thể khôi phục:
| Định Dạng | Có Thể Khôi Phục? | Ví Dụ | Phương Thức Khôi Phục |
|---|---|---|---|
| Văn Bản Thuần | Có | password: hunter2 | Đọc tệp |
| Base64 | Có | cGFzc3dvcmQ= | base64 --decode |
| Mã hóa hai chiều (AES) | Có | ENC[AES256:...] | Giải mã bằng khóa |
| Hàm băm một chiều (bcrypt) | Không | $2b$12$... | Không thể đảo ngược; phải brute-force |
Văn bản thuần, base64 và mã hóa hai chiều: đều có thể khôi phục. Một lần dump cơ sở dữ liệu thông tin đăng nhập sẽ cung cấp cho kẻ tấn công tất cả mật khẩu dạng rõ, cho tất cả người dùng, cùng lúc. Một lần vi phạm; toàn bộ dữ liệu bị lộ.
Ví dụ Mailman 2.x
Mailman 2.x (trình quản lý danh sách gửi thư GNU): lưu mật khẩu người đăng ký dạng văn bản thuần. Email nhắc nhở mật khẩu hàng tháng: gửi mật khẩu dạng rõ cho tất cả người đăng ký. Hai lỗi riêng biệt, cả hai đều thuộc MOAD-0006:
1. Lưu trữ: văn bản thuần trong cơ sở dữ liệu danh sách. Một cuộc tấn công vào máy chủ sẽ lộ toàn bộ mật khẩu người đăng ký.
2. Phát tán: email hàng tháng gửi mật khẩu dạng rõ qua SMTP đến máy chủ email của người đăng ký. Email di chuyển dạng rõ qua nhiều bước SMTP.
Đội ngũ Mailman đã thiết kế cả hai hành vi. Khôi phục là tính năng: người đăng ký có thể lấy lại mật khẩu đã quên. Tên Glass Safe xuất phát từ điều này: chiếc két an toàn chứa thông tin đăng nhập ở dạng có thể đọc được. Bất kỳ ai tiếp cận được két an toàn đều có thể đọc toàn bộ nội dung cùng lúc.
Nguyên tắc Đã Bị Đánh Cắp
Một thông tin đăng nhập được lưu trữ ở dạng có thể khôi phục chính là một thông tin đăng nhập đã bị đánh cắp. Kẻ tấn công chưa đến. Vụ rò rỉ chưa xảy ra. Nhưng kiến trúc đảm bảo: khi một vụ rò rỉ xảy ra, tất cả thông tin đăng nhập sẽ rơi vào tay kẻ tấn công cùng lúc. Không có vụ rò rỉ nào xảy ra riêng lẻ; mọi thông tin đăng nhập trong kho lưu trữ có thể khôi phục đều chuyển sang cho kẻ tấn công trong cùng một thao tác.
MOAD-0006 so với MOAD-0004
MOAD-0004 (Bí mật Đã Ghi Log): thông tin đăng nhập được ghi vào log một cách vô tình. Việc ghi log không phải là mục đích; đó là tác dụng phụ khi bật ghi log header để gỡ lỗi.
MOAD-0006 (Glass Safe): thông tin đăng nhập được lưu trữ ở dạng có thể khôi phục theo thiết kế. Khôi phục là mục đích. Tính năng nhắc nhở mật khẩu yêu cầu lưu trữ mật khẩu. Tính năng hiển thị mật khẩu yêu cầu lưu trữ mật khẩu. Cam kết kiến trúc về khả năng khôi phục đã tạo ra lỗ hổng.
Sự khác biệt chỉ trong một dòng: MOAD-0004 đưa thông tin đăng nhập vào log một cách vô tình; MOAD-0006 lưu trữ thông tin đăng nhập ở dạng có thể khôi phục theo mục đích. Các bản sửa lỗi hoạt động ở các lớp khác nhau.
Cấu Trúc so với Vô Tình
Sự khác biệt về kiến trúc giữa MOAD-0006 và MOAD-0004 quyết định chiến lược sửa lỗi. Ghi log ngẫu nhiên: sửa lớp serialization. Lưu trữ được thiết kế để khôi phục: thiết kế lại tính năng yêu cầu khôi phục.
Tại sao bcrypt hoạt động
Hàm băm một chiều chấp nhận mật khẩu và tạo ra bản tóm tắt có độ dài cố định. Với bản tóm tắt, mật khẩu gốc không thể được khôi phục. Không phải 'khó khôi phục': không thể đảo ngược. Hàm chỉ chạy theo một hướng.
Ba tính chất cần thiết cho việc lưu trữ thông tin xác thực:
1. Một chiều (kháng preimage). Cho trước hash(password), không có thuật toán nào khôi phục được password nhanh hơn brute-force. bcrypt, scrypt và argon2 đều đáp ứng tính chất này.
2. Salt. Một giá trị ngẫu nhiên được thêm vào trước mật khẩu trước khi băm. Cùng một mật khẩu, salt khác nhau → hash khác nhau. Mục đích: chống lại rainbow tables (từ điển hash đã tính sẵn). Không có salt: kẻ tấn công chỉ cần tính hash('password123') một lần và kiểm tra đồng thời trên 1 triệu người dùng. Có salt: mỗi người dùng có một giá trị hash duy nhất ngay cả khi dùng chung mật khẩu.
3. Chậm theo thiết kế. bcrypt chấp nhận work factor. Work factor càng cao → càng nhiều vòng lặp → thời gian tính toán mỗi lần băm càng lâu. Đăng nhập: 300ms để băm một lần. Chấp nhận được. Brute-force: 300ms mỗi lần thử. Với 1 tỷ lần thử: 9,5 năm cho mỗi mật khẩu. Không thể chấp nhận đối với kẻ tấn công. Sự chậm này là một tính năng, không phải lỗi.
import bcrypt
# Lưu trữ: băm một chiều kèm salt
def store_password(plaintext: str) -> bytes:
return bcrypt.hashpw(plaintext.encode(), bcrypt.gensalt(rounds=12))
# Xác minh: băm chuỗi ứng viên & so sánh các bản tóm tắt
def verify_password(plaintext: str, stored_hash: bytes) -> bool:
return bcrypt.checkpw(plaintext.encode(), stored_hash)
# KHÔNG BAO GIỜ lưu trữ: mật khẩu dạng văn bản thuần
# KHÔNG BAO GIỜ khôi phục được: văn bản gốc từ hash
# Đặt lại mật khẩu, không phải nhắc nhở mật khẩu
Sự đánh đổi
Băm một chiều khiến việc khôi phục mật khẩu trở nên bất khả thi. Người dùng quên mật khẩu không thể nhận lại mật khẩu cũ. Không thể tồn tại email nhắc nhở mật khẩu. Trải nghiệm người dùng thay đổi: 'quên mật khẩu? đặt lại.' Không phải là sự suy giảm: đây là ranh giới bảo mật. Hệ thống không thể khôi phục mật khẩu thì cũng không thể rò rỉ mật khẩu.
Một vụ rò rỉ cơ sở dữ liệu lộ các hash bcrypt: tất cả hash đều hiển thị, nhưng không có mật khẩu nào lộ ra. Kẻ tấn công phải brute-force từng hash riêng lẻ, với 300ms mỗi lần thử, và salt riêng cho từng người dùng ngăn chặn các bảng tra cứu được tính trước. Một vụ rò rỉ lộ mật khẩu dạng văn bản gốc: toàn bộ thông tin bị lộ ngay lập tức.
Mã hóa mạnh không đủ
Một cuộc kiểm toán bảo mật phát hiện hệ thống lưu trữ thông tin đăng nhập. Mật khẩu được lưu trữ bằng mã hóa AES-256-CBC với khóa phía máy chủ. Báo cáo kiểm toán đánh dấu đây là lỗi Glass Safe.
Nhóm kỹ thuật trả lời: 'AES-256 là thuật toán mã hóa đối xứng mạnh nhất hiện có. Khóa được lưu trong mô-đun bảo mật phần cứng. Không kẻ tấn công nào có thể giải mã các mật khẩu này.'