Thuật toán BCrypt để mã hoá mật khẩu
Back To Blogs1. Khái niệm
MD5 và SHA256 đều tương đối mạnh mẽ, tuy nhiên để sử dụng cho việc mã hoá mật khẩu vẫn chưa thực sự tốt, vì với cùng một chuỗi thì MD5 và SHA256 sẽ cho ra các chuỗi cố định, như vậy kẻ tấn công có thể tạo ra một cơ sở dữ liệu chứa các chuỗi và mã hoá, như vậy thì kẻ tấn công có được chuỗi mã hoá của người dùng thì vẫn có thể tìm ra được mật khẩu rõ của người dùng.
Để khắc phục vấn đề này chúng ta có thể sử dụng thuật toán BCrypt.
BCrypt là một thuật toán mã hóa mật khẩu được thiết kế để bảo vệ dữ liệu nhạy cảm, đặc biệt là mật khẩu người dùng. Nó được phát triển vào năm 1999 bởi Niels Provos và David Mazières, dựa trên thuật toán mã hóa Blowfish, một thuật toán mã hóa đối xứng mạnh mẽ. Tuy nhiên thay vì dùng để mã hóa dữ liệu, BCrypt sử dụng Blowfish để tạo ra một hàm băm một chiều, nghĩa là từ đầu vào (mật khẩu) có thể tạo ra đầu ra (chuỗi băm), nhưng không thể đảo ngược để lấy lại mật khẩu ban đầu.
BCrypt sử dụng một hàm băm (hash function) kết hợp với một công thức mã hóa mật khẩu được gọi là “salt” để tạo ra một mã băm mật khẩu.
Vì salt ngẫu nhiên đảm bảo mỗi chuỗi băm là duy nhất, ngay cả với cùng một mật khẩu nên nó chống được rainbow table
Một điểm đáng chú ý của BCrypt là nó có thể điều chỉnh độ phức tạp tính toán của thuật toán bằng cách sử dụng một tham số gọi là “cost factor”. Tham số này quyết định số lần lặp lại quá trình băm mật khẩu và ảnh hưởng đến thời gian tính toán cần thiết để mã hóa mật khẩu. Điều này làm cho việc tấn công bằng cách thử và sai (brute force) trở nên đáng kể khó khăn hơn.
2. Nguyên lý hoạt động
Bcrypt hoạt động với ba yếu tố chính:- Mật khẩu (password): Chuỗi mà người dùng nhập vào.
- Salt: Một chuỗi ngẫu nhiên được tạo ra để đảm bảo rằng cùng một mật khẩu sẽ tạo ra các chuỗi băm khác nhau mỗi lần.
- Work factor (hay cost factor): Một tham số điều chỉnh độ phức tạp tính toán, thường được biểu thị dưới dạng số mũ của 2 (ví dụ: 10 nghĩa là 2^10 vòng lặp). Điều này làm cho việc băm chậm hơn, từ đó tăng độ khó cho kẻ tấn công.
Các bước BCrypt xử lý mật khẩu:
- Bước 1: Tạo salt (muối) BCrypt sử dụng một chuỗi salt ngẫu nhiên để gia tăng tính bảo mật của giá trị băm. Salt thường có độ dài là 16 byte (128 bits) và được tạo ra một cách ngẫu nhiên cho mỗi mật khẩu. Salt này đảm bảo rằng cùng một mật khẩu sẽ không bao giờ cho ra cùng một chuỗi băm, giúp chống lại các bảng rainbow table. Để salt value tự sinh ra ngẫu nhiên, BCrypt sẽ lưu trữ salt trong chính hash value của nó.
- Bước 2: Chuẩn bị yếu tố công việc (work factor) yếu tố công việc (work factor) đại diện cho số lượng vòng lặp mà BCrypt sẽ thực hiện để băm mật khẩu. Giá trị này được xác định bởi một tham số gọi là log_rounds (ví dụ: log_rounds = 10). Giá trị log_rounds càng cao thì quá trình băm càng mất thời gian và tài nguyên tính toán càng nhiều.
- Bước 3: Băm mật khẩu mật khẩu của người dùng được kết hợp với salt và sau đó được đưa qua một chuỗi vòng lặp. Mỗi vòng lặp bao gồm hai giai đoạn chính: giai đoạn mở rộng (expansion) và giai đoạn trộn (mixing).
-
- Giai đoạn mở rộng (expansion): Mật khẩu kết hợp với salt và được đưa qua hàm Blowfish, trong đó các phần tử của mật khẩu và salt được kết hợp và trộn lẫn với nhau.
-
- Giai đoạn trộn (mixing): Kết quả của giai đoạn mở rộng được sử dụng để tạo ra một chuỗi trạng thái (state) ban đầu. Quá trình này được lặp lại nhiều lần để tạo ra một chuỗi trạng thái cuối cùng.
- Bước 4: Tạo giá trị băm sau khi hoàn thành các vòng lặp, chuỗi trạng thái cuối cùng được sử dụng để tạo ra giá trị băm cuối cùng. Giá trị băm này là kết quả cuối cùng mà người dùng sẽ lưu trữ trong cơ sở dữ liệu.
-
- Kết quả cuối cùng là một chuỗi dài 60 ký tự, bao gồm: work factor (ví dụ: $2a$10$), salt (22 ký tự được mã hóa bằng base64), chuỗi băm thực tế (31 ký tự).
Ví dụ: $2a$10$08CqeP2HQqD3NnPu8Xuom.y4mD7yGKkpGi024MmYDWunGrcsbCpUu
-
- Khi người dùng đăng nhập quá trình băm sẽ được lặp lại với mật khẩu đã nhập và salt lưu trữ trong cơ sở dữ liệu. Kết quả của quá trình băm này được so sánh với giá trị băm đã lưu trữ. Nếu chúng khớp mật khẩu được xác nhận là đúng, nếu không quá trình xác thực sẽ thất bại và người dùng sẽ không được cho phép truy cập.
- Ví dụ: Cách để BCrypt kiểm tra xem mật khẩu người dùng có khớp với mật khẩu đã lưu trong cơ sở dữ liệu.

- Mật khẩu được người dùng tạo khi đăng nhập lần đầu tiên là
password123
và chuỗi đã được mã hoá và lưu trong cơ sở dữ liệu là$2a$10$XyZ789kLmN12pQrSabc123xyz...
- Hàm
BCrypt.checkpw(plainTextPassword, hashedPassword)
Đây là hàm kiểm tra trong thư viện BCrypt: Hàm này nhận hai tham số là nhận mật khẩu từ người dùng khi đăng nhập password123
và chuỗi băm đã lưu trong cơ sở dữ liệu $2a$10$XyZ789kLmN12pQrSabc123xyz...
Hàm sẽ so sánh mật khẩu đầu vào với chuỗi băm để kiểm tra xem chúng có khớp nhau không
- Thông tin thuật toán BCrypt:
Algorithm: 2a: Phiên bản của BCrypt (ở đây là phiên bản $2a)
Number of Rounds: 10: Work factor là 10, tương đương với 2^10 = 1024 vòng lặp trong thuật toán Blowfish
Salt XyZ789kLmN12pQrS
: Chuỗi salt 22 ký tự được trích xuất từ chuỗi băm (hashedPassword).
BCrypt.hashpw("password123", XyZ789kLmN12pQrS)
: Đây là bước giả định mô tả cách mật khẩu gốc (password123) được băm với salt cụ thể (XyZ789kLmN12pQrS) và work factor 10 để tạo ra chuỗi băm.
- Các bước hoạt động:
-
- Bước 1: Khi người dùng nhập tên đăng nhập và mật khẩu là
password123
- Bước 1: Khi người dùng nhập tên đăng nhập và mật khẩu là
-
- Bước 2: Hàm
BCrypt.checkpw
được gọi với hai tham số mật khẩu người dùng vừa nhậppassword123
và chuỗi băm được lưu trong cơ sở dư liệu ứng với tên đăng nhập$2a$10$XyZ789kLmN12pQrSabc123xyz...
- Bước 2: Hàm
Hàm này sẽ : Trích xuất salt và work factor từ chuỗi băm (ở đây salt là XyZ789kLmN12pQrS
và work factor 10
)
Băm mật khẩu vừa nhập password123
bằng salt XyZ789kLmN12pQrS
và work factor là 10 được một chuỗi băm mới cùng định dạng và độ dài (60 ký tự) như chuỗi băm đã lưu, bao gồm phiên bản, work factor, salt, và phần băm mật khẩu
-
- Bước 3: So sánh kết quả nếu kết quả của chuỗi băm mới giống hệt chuỗi băm đã lưu trong cơ sở dữ liệu
$2a$10$XyZ789kLmN12pQrSabc123xyz...
thì có nghĩa là mật khẩu vừa nhập là đúng, còn nếu không khớp với chuỗi đã lưu thì mật khẩu vừa nhập là sai
- Bước 3: So sánh kết quả nếu kết quả của chuỗi băm mới giống hệt chuỗi băm đã lưu trong cơ sở dữ liệu
- Dù BCrypt.checkpw tạo ra một chuỗi băm mới để so sánh, quá trình này vẫn là một chiều (one-way). Không thể suy ra mật khẩu gốc từ chuỗi băm, ngay cả khi biết salt và work factor
- Khác với khi tạo chuỗi băm ban đầu (dùng salt ngẫu nhiên), BCrypt.checkpw sử dụng lại salt đã được nhúng trong chuỗi băm. Điều này đảm bảo tính nhất quán và không cần lưu salt riêng biệt
3. Áp dụng
- Thêm thư viện jBCrypt vào
pom.xml
.
<dependency> <groupId>org.mindrot</groupId> <artifactId>jbcrypt</artifactId> <version>0.4</version> </dependency>
import org.mindrot.jbcrypt.BCrypt; public class BCryptExample { // Hàm mã hóa mật khẩu public static String hashPassword(String plainTextPassword) { // Tạo salt và băm mật khẩu với cost factor mặc định (10) // Bạn có thể chỉ định cost factor bằng cách truyền tham số, ví dụ: BCrypt.gensalt(12) String hashedPassword = BCrypt.hashpw(plainTextPassword, BCrypt.gensalt()); return hashedPassword; } // Hàm xác minh mật khẩu public static boolean verifyPassword(String plainTextPassword, String hashedPassword) { // So sánh mật khẩu gốc với giá trị băm return BCrypt.checkpw(plainTextPassword, hashedPassword); } public static void main(String[] args) { // Mật khẩu gốc String password = "password123"; // Mã hóa mật khẩu String hashedPassword = hashPassword(password); System.out.println("Hashed Password: " + hashedPassword); // Xác minh mật khẩu boolean isMatch = verifyPassword(password, hashedPassword); System.out.println("Password match: " + isMatch); } }
4. Tóm lại
BCrypt là một thuật toán băm mật mã rất hiệu quả, cung cấp những ưu điểm đáng kể cho nhà phát triển web khi cần lưu trữ mật khẩu một cách an toàn. Tính linh hoạt, khả năng chống lại các cuộc tấn công và dễ tích hợp của nó làm cho nó trở thành sự lựa chọn ưa thích để bảo vệ dữ liệu nhạy cảm của người dùng trong các ứng dụng web. Vậy nên trong các dự án sắp tới, nếu cần bảo mật dữ liệu người dùng các bạn hãy cân nhắc sử dụng BCrypt.