
Argon2 là thuật toán băm mật khẩu được chọn làm chiến thắng cuộc thi Password Hashing Competition (PHC) vào ngày 20/7/2015, sau khi 24 mô hình tranh tấn bị loại dần còn 9 finalist rồi mới chọn ra một người thắng duy nhất. Nếu bạn đang lưu mật khẩu người dùng, thuật toán bạn nên dùng chính là Argon2, mặc định là biến thể Argon2id. Bài viết này giải thích cơ chế bên trong, các tham số cấu hình và cách đo đạc để chọn thông số an toàn.
Vì sao Argon2 khác bcrypt và PBKDF2
Điểm yếu cốt lõi của thuật toán băm mật khẩu cũ là chúng gần như chỉ tiêu tốn CPU. Kẻ tấn công có ASIC hoặc GPU chuyên dụng thì băm hàng triệu mật khẩu mỗi giây với chi phí điện rất thấp. PBKDF2 không có khái niệm tốn bộ nhớ nên rất dễ bị tăng tốc; bcrypt tốn khoảng 4 KB bộ nhớ cố định, tốt hơn PBKDF2 nhưng vẫn không đủ khó với thiết bị hiện đại.
Argon2 là thuật toán memory-hard, buộc kẻ tấn công phải dành cả bộ nhớ cho mỗi lần băm thay vì chỉ tiêu tốn số đơn vị xử lý. Theo tài liệu đặc tả chính thức, Argon2 lấp đầy 1 GB RAM trong chưa đầy một giây, nhanh hơn cả băng thông bộ nhớ hiện đại, khiến GPU và ASIC không thể mở rộng tuyến tính như các hàm băm chỉ dùng CPU. Bạn có thể tải bản đặc tả tại argon2-specs.pdf và lịch sử cuộc thi tại trang Password Hashing Competition.

Sơ đồ trên mô tả luồng lưu trữ an toàn: phía người dùng gửi mật khẩu qua kênh được mã hóa, phía máy chủ chỉ giữ lại giá trị băm. Ngay cả khi kẻ tấn công lấy được toàn bộ cơ sở dữ liệu gồm giá trị băm và cả muối (salt), họ vẫn không có mật khẩu gốc để dùng lại cho các hệ thống khác.
Ba biến thể Argon2d, Argon2i và Argon2id
Argon2 có ba chế độ truy cập bộ nhớ khác nhau, ứng dụng cho ba tình huống khác nhau:
- Argon2d: truy cập bộ nhớ phụ thuộc vào mật khẩu, sinh dữ liệu ngẫu nhiên tốc độ nhất nhưng dễ bị tấn công kênh bên (side-channel) vì kiểu truy cập bộ nhớ lộ theo dữ liệu.
- Argon2i: truy cập bộ nhớ độc lập với mật khẩu và dữ liệu đầu vào, an toàn hơn trước side-channel nhưng chậm hơn vì phải quét bộ nhớ nhiều lượt hơn.
- Argon2id: bản lai, nửa đầu lượt đầu dùng kiểu Argon2i rồi chuyển sang kiểu Argon2d cho các lượt còn lại, vừa chống side-channel vừa giữ tốc độ của Argon2d.
Trường type trong định dạng chuỗi băm lấy giá trị 0 cho Argon2d, 1 cho Argon2i và 2 cho Argon2id. RFC 9106 khuyến nghị dùng Argon2id khi bạn không chắc chắn về môi trường thực thi hoặc khi có nguy cơ tấn công kênh bên. Đây cũng chính là lựa chọn mặc định trong các thư viện phổ biến như argon2-cli và thư viện @node-rs/argon2.
Ba tham số bạn phải chỉnh
Argon2 nhận ba tham số chi phí chính, cùng một cặp muối 128 bit và thẻ đầu ra 256 bit:
m(memory cost): khối bộ nhớ tính bằng KiB. Giá trị hợp lệ nằm trong khoảng từ 8 lầnpđến 2 mũ 32 trừ 1 KiB. Đây là tham số quan trọng nhất vì nó quyết định chi phí bộ nhớ mà kẻ tấn công phải chịu.t(time cost): số lượt lặp, nhân trực tiếp với thời gian băm. Giá trị lớn hơn nghĩa là chậm hơn và an toàn hơn.p(parallelism): số luồng xử lý song song, giúp tận dụng nhiều lõi CPU.

Ma trận khối bộ nhớ chính là nơi phát sinh độ khó: kẻ tấn công không thể tính trước khối cần dùng nếu không có toàn bộ bộ nhớ cùng lúc. Chính vì vậy, thông số bộ nhớ phải được cân nhắc dựa trên RAM thực tế của máy chủ, không chỉ dựa trên bộ vi xử lý.
Bảng thông số khuyến nghị chính thức
Hai nguồn tham chiếu đáng tin cậy đưa ra các bộ thông số khác nhau vì khác nhau về giả định tài nguyên. Bạn nên chọn theo tình huống của hệ thống:
| Nguồn | memory cost | time cost | parallelism | Ghi chú |
|---|---|---|---|---|
| RFC 9106 lựa chọn 1 | 2^21 KiB (2 GiB) | 1 | 4 | Mặc định cho mọi môi trường |
| RFC 9106 lựa chọn 2 | 2^16 KiB (64 MiB) | 3 | 4 | Dành cho môi trường thiếu bộ nhớ |
| OWASP tối thiểu | 19.456 KiB (19 MiB) | 2 | 1 | Phương án tiết kiệm |
| OWASP thay thế | 47.104 KiB (46 MiB) | 1 | 1 | Ưu tiên hơn về bộ nhớ |
Cả hai nguồn đều đặt mục tiêu thời gian băm một mật khẩu dưới một giây. Hướng dẫn đầy đủ nằm tại Password Storage Cheat Sheet của OWASP, phần định nghĩa ba tham số chi phí ở mục 3.1 của RFC 9106.
Đọc hiểu các con số hiệu năng
Trang README của dự án PHC công bố kết quả đo với bộ tham số 65.536 KiB, 3 lượt, 1 luồng cho thời gian băm khoảng 0,188 giây mỗi mật khẩu. Ở cấu hình rất nhỏ, một lượt băm với 1 MiB bộ nhớ và một luồng mất khoảng 0,004 giây, tức khoảng 250 mật khẩu mỗi giây trên cùng một luồng. Ngược lại, khi tăng lên 4.096 MiB bộ nhớ với hai luồng, mỗi lượt băm mất khoảng 13 giây. Chi phí bộ nhớ vì vậy chi phối mạnh hơn số lượt lặp, nên đừng ham tăng t mà bỏ qua m.
Chi tiết hiệu năng trong đặc tả cho thấy Argon2d đạt 1,3 chu kỳ mỗi byte ở một luồng và giảm xuống 0,6 chu kỳ mỗi byte ở bốn luồng, tương ứng băng thông 3,8 GB/s lên 5,4 GB/s. Bạn có thể đối chiếu bảng số liệu gốc trong README dự án phc-winner-argon2.
So sánh nhanh với bcrypt và PBKDF2
OWASP khuyến nghị bcrypt với work factor tối thiểu 10 nhưng lưu ý bcrypt chỉ dùng được 72 byte đầu tiên của mật khẩu, còn PBKDF2-HMAC-SHA256 cần tối thiểu 600.000 lượt, PBKDF2-HMAC-SHA512 tối thiểu 220.000 lượt, và bản SHA-1 cũ cần 1.400.000 lượt. Chi tiết về giới hạn của bcrypt được ghi nhận tại trang bcrypt trên Wikipedia.
- Argon2id: tốn bộ nhớ theo cấu hình, chống được cả GPU lẫn ASIC, thời gian băm điều chỉnh được.
- bcrypt: tốn khoảng 4 KB bộ nhớ, không tăng theo work factor, cắt mật khẩu ở 72 byte.
- PBKDF2: không memory-hard, dễ bị tốc độ hóa trên phần cứng chuyên dụng.
Điểm quan trọng khi chuyển đổi hệ thống cũ là ghi tham số vào chuỗi băm đã lưu, dạng $argon2id$v=19$m=65536,t=3,p=4$salt$hash. Nhờ vậy mỗi mật khẩu mang thông số riêng, bạn tăng cấu hình cho mật khẩu mới vẫn xác thực được mật khẩu cũ. Khi đăng nhập thành công, hãy băm lại bằng thông số mới để người dùng tự chuyển dần.
