Lời khuyên "băm nó đi" che giấu nhiều công việc khác nhau. Phát hiện tệp tải về bị hỏng, đặt tên tệp theo nội dung, chứng minh webhook đến từ cổng thanh toán và lưu mật khẩu đều dùng hàm băm, nhưng mỗi việc cần một tính chất khác, và chọn sai là nguyên nhân của nhiều sự cố bảo mật thật. Bài này đi qua từng việc với ví dụ cụ thể và các giá trị bạn có thể tự tính lại bằng công cụ tạo mã băm.
Hàm băm hứa hẹn điều gì
Hàm băm mật mã biến mọi đầu vào thành một giá trị có kích thước cố định (32 byte với SHA-256) với ba tính chất:
- Kháng tiền ảnh. Có giá trị băm, bạn không tính ngược ra được đầu vào tạo ra nó.
- Kháng tiền ảnh thứ hai. Có một đầu vào, bạn không tìm được đầu vào khác cho cùng giá trị băm.
- Kháng va chạm. Bạn không tìm được hai đầu vào bất kỳ có cùng giá trị băm.
Nó không hứa giữ bí mật cho đầu vào ngắn. Ai cũng có thể băm mọi mật khẩu phổ biến rồi so sánh, nên câu "không đảo ngược được giá trị băm" chỉ đúng khi đầu vào không đoán được.
Cùng một văn bản luôn cho cùng giá trị băm, và đổi một ký tự sẽ cho giá trị hoàn toàn khác. Câu The quick brown fox jumps over the lazy dog có SHA-256 d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592; thay chữ cuối thành cog thì giá trị băm khác hẳn: e4c4d8f3bf76b692de791a173e05321150f7a345b46484fe427f6acc7ecc81be.
Việc 1: phát hiện hỏng dữ liệu vô tình
Một tệp tải về, bản sao lưu hay tệp chép qua mạng chập chờn có thể mất hoặc lật bit. Checksum nào cũng bắt được: CRC-32 (dùng trong ZIP, PNG, gzip), MD5 hay SHA-256. CRC-32 nhanh nhất và được thiết kế đúng cho lỗi ngẫu nhiên; giá trị kiểm tra chuẩn của nó, CRC của 123456789, là CBF43926.
Điểm mấu chốt là chữ "vô tình". CRC-32 là hàm tuyến tính, nên ai cũng có thể sửa tệp rồi chỉnh bốn byte để giữ nguyên CRC. Nếu tệp đến từ nơi kẻ tấn công có thể can thiệp, bạn cần việc tiếp theo.
Việc 2: toàn vẹn trước kẻ tấn công
Khi bạn so bộ cài vừa tải với giá trị SHA-256 công bố trên trang của dự án, bạn dựa vào tính kháng va chạm và kháng tiền ảnh thứ hai. Lúc này thuật toán rất quan trọng:
- MD5 đã có va chạm thực tế từ năm 2004, và giờ tạo được trên một máy tính bình thường. Hai tệp khác nhau cùng MD5 rất dễ làm ra.
- SHA-1 sụp đổ năm 2017, khi các nhà nghiên cứu công bố hai tệp PDF khác nhau có cùng SHA-1.
- SHA-256, SHA-384, SHA-512 và SHA-3 chưa có tấn công thực tế nào được biết. SHA-256 là lựa chọn thông dụng; SHA-512 mạnh tương đương và thường nhanh hơn trên CPU 64 bit.
Công cụ kiểm tra mã băm tệp so sánh giúp bạn với giá trị đã công bố. Hãy nhớ checksum chỉ có ích khi bạn lấy nó từ một nơi khác, đáng tin, chứ không phải cùng chỗ với tệp.
Việc 3: chứng minh ai gửi tin nhắn (HMAC)
Một webhook đến báo "đã nhận thanh toán". Ai trên internet cũng gửi được yêu cầu đó, nên nhà cung cấp ký nó. Chỉ một hàm băm thì không làm được việc này: nếu chữ ký là SHA-256(message), kẻ tấn công cũng tự tính được. Bạn cần một khóa bí mật, và cách dựng đúng là HMAC:
HMAC(key, message) = H((key ⊕ opad) || H((key ⊕ ipad) || message))Sao không đơn giản là SHA-256(key || message)? Vì SHA-256 (cũng như MD5, SHA-1) bị tấn công mở rộng độ dài: từ giá trị băm của key || message, kẻ tấn công không biết khóa vẫn tính được giá trị băm của key || message || padding || phần thêm, và giả mạo được tin nhắn dài hơn. Hai lớp băm lồng nhau của HMAC bịt lỗ hổng đó. (SHA-3 không bị tấn công mở rộng độ dài, nhưng HMAC vẫn là cách chuẩn để dùng khóa.)
Giá trị ví dụ: với khóa key và câu về con cáo ở trên, HMAC-SHA256 là f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8. Trong mã máy chủ, hãy so chữ ký nhận được bằng hàm so sánh thời gian hằng, không dùng ==, để thời gian chạy không để lộ bao nhiêu ký tự đã khớp.
Việc 4: mật khẩu (không dùng hàm nào ở trên)
Hàm băm nhanh lại chính là thứ sai cho mật khẩu. Một card đồ họa hiện đại tính hàng tỷ giá trị MD5 hay SHA-256 mỗi giây, nên kẻ tấn công lấy được bảng mật khẩu băm bằng SHA-256 có thể thử mọi mật khẩu phổ biến và mọi tổ hợp ngắn rất nhanh, còn các mật khẩu giống nhau cho giá trị băm giống nhau giữa các người dùng.
Lưu mật khẩu cần một hàm cố tình chậm và tốn bộ nhớ, với salt ngẫu nhiên cho từng người dùng: Argon2id (khuyến nghị hiện tại của OWASP Password Storage Cheat Sheet), scrypt hoặc bcrypt. Framework web của bạn gần như chắc chắn đã có sẵn. Mật khẩu ngẫu nhiên dài từ công cụ tạo mật khẩu cũng giúp ích, nhưng lựa chọn phía máy chủ quan trọng hơn.
Chọn nhanh
| Việc | Dùng | Tránh |
|---|---|---|
| Phát hiện hỏng dữ liệu vô tình | CRC-32, SHA-256 | |
| Kiểm tra tệp tải về, khử trùng lặp tệp không tin cậy | SHA-256 hoặc SHA-512 | MD5, SHA-1, CRC-32 |
| Ký webhook hay yêu cầu API | HMAC-SHA256 | SHA-256(key + message) |
| Lưu mật khẩu | Argon2id, scrypt, bcrypt | Mọi hàm băm nhanh, kể cả có salt |
| Địa chỉ và chữ ký Ethereum | Keccak-256 | SHA3-256 (phần đệm khác) |
Những lỗi hay gặp
- Băm văn bản kèm dấu xuống dòng ẩn.
echo 'abc' | sha256sumbăm bốn byte. Hãy dùngprintf '%s'hoặcecho -nđể khớp với công cụ. - Lẫn mã hóa ký tự. Cùng một từ ở UTF-8 và UTF-16 cho giá trị băm khác nhau. Hãy thống nhất dùng UTF-8.
- So hex phân biệt hoa thường.
BA78…vàba78…là cùng các byte. Ô so sánh của công cụ bỏ qua chữ hoa và dấu cách. - Nhầm Base64 với hex. Một SHA-256 dài 64 ký tự hex hoặc 44 ký tự Base64; cả hai đều là cùng 32 byte.
- Coi băm là mã hóa. Giá trị băm không giải mã được; nếu cần lấy lại dữ liệu, hãy dùng mã hóa văn bản.
Thử ngay
Dán văn bản vào công cụ tạo mã băm để xem mọi thuật toán cạnh nhau, bật HMAC với một khóa thử để tái tạo chữ ký webhook, và dán một checksum đã công bố vào ô so sánh để xem nó đến từ thuật toán nào.