Các phương pháp lưu trữ & xác thực mật khẩu hiện đại

Trong giai đoạn 2010, nhiều hệ thống thường sử dụng MD5 hoặc SHA1 để lưu mật khẩu — nhanh, tiện nhưng lại cực kỳ thiếu an toàn. Hiện nay, với mức độ tấn công ngày càng tinh vi, các chuẩn lưu trữ và xác thực mật khẩu đã tiến bộ vượt bậc. Bài viết này tổng hợp những kỹ thuật hiện đại, bảo mật và tối ưu nhất mà bạn nên áp dụng để bảo vệ hệ thống của mình, đặc biệt là khi xây dựng website, API hay ứng dụng PHP.

Các phương pháp lưu trữ & xác thực mật khẩu hiện đại

Hạn chế của MD5, SHA1 và các thuật toán băm cũ

MD5, SHA1 và SHA256 bản chất là các hàm băm nhanh, không có cơ chế chống tấn công brute-force hoặc rainbow table. Khi tin tặc có được hash, việc bẻ khóa trở nên quá dễ dàng nhờ GPU hoặc các dàn máy chuyên dụng. Vì vậy, các thuật toán này không còn được khuyến nghị dùng để lưu mật khẩu, chỉ nên dùng cho các mục đích kiểm tra toàn vẹn dữ liệu.

Mật khẩu nên được lưu theo dạng “slow hashing”

Slow hashing là kỹ thuật khiến việc băm mật khẩu trở nên chậm hơn có chủ đích, nhằm ngăn chặn brute-force tốc độ cao. Các thuật toán hiện đại như bcrypt, Argon2 và scrypt được thiết kế dựa trên triết lý này, giúp tăng độ an toàn đáng kể bằng cách tiêu tốn nhiều thời gian hoặc bộ nhớ cho mỗi lần băm.

PHP password_hash & password_verify – Chuẩn hiện đại

Hàm password_hash() tạo ra chuỗi băm an toàn với salt ngẫu nhiên tích hợp sẵn. Hàm password_verify() dùng để kiểm tra mật khẩu người dùng nhập có khớp với hash hay không, đảm bảo tính chính xác và an toàn tối đa. Đây là chuẩn bảo mật được khuyến nghị trong mọi hệ thống PHP hiện nay.

Ví dụ sử dụng password_hash và password_verify (PHP)

Dưới đây là ví dụ đơn giản cho quá trình đăng ký và đăng nhập sử dụng bcrypt qua hàm password_hash:

<?php
// Đăng ký tài khoản: lưu mật khẩu
$passwordPlain = $_POST['password'] ?? '';

// Tạo hash sử dụng bcrypt (mặc định)
$hash = password_hash($passwordPlain, PASSWORD_DEFAULT);

// Lưu $hash vào cột password trong database
// ví dụ: INSERT INTO users (username, password) VALUES (:username, :hash);
?>

Khi người dùng đăng nhập, bạn dùng password_verify để kiểm tra:

<?php
// Lấy mật khẩu người dùng nhập
$passwordInput = $_POST['password'] ?? '';

// Lấy hash đã lưu trong database theo username/email
// ví dụ: SELECT password FROM users WHERE username = :username
$hashFromDb = $user['password'] ?? '';

if (password_verify($passwordInput, $hashFromDb)) {
    // Đăng nhập thành công
    // Xử lý session, token...
} else {
    // Sai mật khẩu
}
?>

Ví dụ sử dụng Argon2id với password_hash

Nếu server của bạn hỗ trợ Argon2, nên ưu tiên PASSWORD_ARGON2ID để có bảo mật tốt hơn:

<?php
$passwordPlain = $_POST['password'] ?? '';

$options = [
    'memory_cost' => 1<<12,  // 4096 KB
    'time_cost'   => 3,       // số vòng lặp
    'threads'     => 2,       // số luồng
];

$hash = password_hash($passwordPlain, PASSWORD_ARGON2ID, $options);

// Lưu $hash vào database như bình thường
?>

Xác thực vẫn sử dụng password_verify giống như với bcrypt, không cần thay đổi thêm gì:

<?php
if (password_verify($passwordInput, $hashFromDb)) {
    // Mật khẩu đúng
}
?>

Ví dụ chuyển từ MD5 sang password_hash

Nếu hệ thống cũ đang lưu mật khẩu dưới dạng MD5, bạn có thể triển khai chiến lược nâng cấp dần khi người dùng đăng nhập:

<?php
$passwordInput = $_POST['password'] ?? '';
$user = getUserByUsername($username); // Lấy user từ DB

$hashFromDb = $user['password'] ?? '';

if (strlen($hashFromDb) === 32 && ctype_xdigit($hashFromDb)) {
    // Có thể đây là MD5 cũ
    if (md5($passwordInput) === $hashFromDb) {
        // Đúng mật khẩu cũ: nâng cấp lên password_hash ngay
        $newHash = password_hash($passwordInput, PASSWORD_DEFAULT);
        updateUserPassword($user['id'], $newHash);
        // Cho đăng nhập
    } else {
        // Sai mật khẩu
    }
} else {
    // Giả định là hash mới (bcrypt/argon2)
    if (password_verify($passwordInput, $hashFromDb)) {
        // Đăng nhập thành công
        // Có thể cân nhắc password_needs_rehash để nâng cấp thêm nếu cần
        if (password_needs_rehash($hashFromDb, PASSWORD_DEFAULT)) {
            $newHash = password_hash($passwordInput, PASSWORD_DEFAULT);
            updateUserPassword($user['id'], $newHash);
        }
    } else {
        // Sai mật khẩu
    }
}
?>

Bcrypt – Lựa chọn ổn định và phổ biến

Bcrypt là thuật toán slow-hash lâu đời nhưng cực kỳ đáng tin cậy. Nó tự động xử lý salt và cho phép cấu hình cost (độ phức tạp). Phần lớn CMS và framework sử dụng bcrypt như một giải pháp mặc định để lưu mật khẩu. Trong PHP, PASSWORD_DEFAULT hiện tại thường là bcrypt (tùy phiên bản), giúp bạn áp dụng dễ dàng mà không cần cấu hình phức tạp.

Argon2 – Tiêu chuẩn mới, mạnh mẽ và bảo mật tối ưu

Argon2 (đặc biệt là Argon2id) được xem là thuật toán lưu mật khẩu tốt trong các lựa chọn hiện nay. Nó chống tấn công GPU mạnh mẽ và cho phép điều chỉnh bộ nhớ, thời gian, số luồng xử lý. Nhiều hệ thống hiện đại đã chuyển sang dùng Argon2 để đạt mức bảo mật cao hơn, đặc biệt cho các ứng dụng cần lưu trữ mật khẩu lâu dài.

Scrypt – Tối ưu chống tấn công phần cứng

Scrypt được thiết kế để chống lại các hệ thống crack mật khẩu sử dụng phần cứng chuyên dụng như FPGA hay ASIC. Nhờ yêu cầu bộ nhớ cao, tin tặc khó thể brute-force hàng loạt hash cùng lúc. Scrypt thường được dùng trong một số thư viện, hệ thống xác thực và thậm chí trong một số cơ chế tiền mã hóa.

Bảng so sánh các phương pháp lưu trữ mật khẩu phổ biến

Bảng dưới đây giúp bạn hình dung nhanh ưu nhược điểm của từng phương pháp lưu trữ mật khẩu:

Thuật toán Loại Tốc độ Chống brute-force Salt tích hợp Mức khuyến nghị
MD5 Hash nhanh Rất nhanh Rất kém Không Không dùng cho mật khẩu
SHA1/SHA256 Hash nhanh Rất nhanh Kém Không Không khuyến nghị cho mật khẩu
Bcrypt Slow hash Chậm (có thể cấu hình) Tốt Có (ngầm định) Khuyến nghị, phổ biến nhất
Argon2id Slow hash, memory-hard Chậm (có thể cấu hình) Rất tốt Rất khuyến nghị nếu server hỗ trợ
Scrypt Slow hash, memory-hard Chậm (tốn RAM) Rất tốt Phụ thuộc implement Tốt, nhưng ít phổ biến hơn bcrypt/Argon2

Không bao giờ tự tạo salt hoặc tự code thuật toán

Nhiều lập trình viên tự viết hàm băm, tự tạo salt hoặc xử lý thủ công. Đây là sai lầm phổ biến. Những hàm hiện đại như password_hash() đã xử lý toàn bộ phần này an toàn hơn rất nhiều, bao gồm cả salt và chi tiết thuật toán. Tự code dẫn đến vô số lỗ hổng khó lường, đặc biệt khi không am hiểu sâu về mật mã học.

Lựa chọn thuật toán nào cho hệ thống hiện nay?

Đối với PHP và đa số web app:

  • Sử dụng password_hash() với PASSWORD_DEFAULT (thường là bcrypt) nếu bạn muốn sự ổn định, dễ cấu hình.
  • Nếu server hỗ trợ, ưu tiên PASSWORD_ARGON2ID để có độ bảo mật cao hơn.

Đối với các ngôn ngữ khác:

  • Ưu tiên dùng Argon2 hoặc bcrypt thông qua các thư viện uy tín.
  • Tránh dùng MD5, SHA1, SHA256 cho việc lưu mật khẩu, kể cả khi có salt thủ công.

Kết luận

Bảo mật mật khẩu không còn là câu chuyện dùng MD5 hay SHA1 như thời 2010. Hệ thống hiện đại cần các phương pháp slow hashing như bcrypt, Argon2 hoặc scrypt để đảm bảo an toàn. Khi sử dụng password_hash và password_verify, bạn gần như đã tuân thủ đầy đủ các chuẩn bảo mật hiện đại nhất, đồng thời dễ dàng nâng cấp thuật toán trong tương lai mà không cần thay đổi logic ứng dụng quá nhiều.

Bình luận


  • Không có bình luận.

Init Toolbox

Nhấn Ctrl + \ trên máy tính, hoặc vuốt sang trái ở bất kỳ đâu trên mobile.

Đăng nhập





Đang tải...