Di chuyển sang password_verify

Gần đây tôi đã cập nhật một trang web được viết cách đây rất lâu mà không được chạm vào một cách có ý nghĩa trong nhiều năm. Ngoài công việc thực tế tôi được yêu cầu, tôi đã nhân cơ hội cập nhật các thói quen băm mật khẩu.
Trang web này cũ đến mức mật khẩu được lưu trữ bằng cách sử dụng băm MD5. và điều đó không thực sự đủ tốt ngày hôm nay. Vì vậy, tôi đã bao gồm việc cập nhật lên bcrypt băm với password_hash () và password_verify () trong tuyên bố công việc của mình.
Tôi đã thực hiện quá trình này trước đây, nhưng dường như không có tài liệu nào, vì vậy tôi nghĩ rằng tôi đã viết ra các bước tôi đã thực hiện trong trường hợp nó giúp được bất kỳ ai khác.
Cập nhật mật khẩu hiện có trong cơ sở dữ liệu
Điều đầu tiên tôi làm là băm tất cả mật khẩu trong cơ sở dữ liệu sang bcrypt bằng password_hash. Vì mật khẩu hiện tại được lưu trữ ở dạng băm, chúng tôi không có mật khẩu văn bản gốc, vì vậy chúng tôi kết thúc bằng băm bcrypt chứa băm MD5. Điều này là ổn vì chúng ta có thể xử lý điều này trong quá trình đăng nhập.
Bản cập nhật này là một tập lệnh PHP một lần:
$sql = 'SELECT id, password FROM user';
$rs = $database->execute($sql);
$rows = $rs->GetArray();
foreach ($rows as $row) {
$sql = 'UPDATE user SET password = ? WHERE id = ?';
$database->execute($sql, [
password_hash($row[['password'], PASSWORD_DEFAULT),
$row['id'],
]);
}
echo "Passwords updated\n";
Trang web này sử dụng ADOdb vì vậy tôi chỉ tiếp tục sử dụng nó. Các nguyên tắc áp dụng bất kể bạn đang sử dụng PDO hay bất kỳ thư viện trừu tượng hóa cơ sở dữ liệu nào khác.
Tôi cũng đã phải cập nhật lược đồ cơ sở dữ liệu và thay đổi cột mật khẩu từ varchar (32) thành varchar (255). Trang 255 hướng dẫn được đề xuất bởi trang hướng dẫn PHP vì nó cho phép thuật toán thay đổi lại.
Đang cập nhật Đăng nhập
Mã xác thực cần cập nhật để xử lý mật khẩu bcrypt. Hiện tại nó trông như thế này:
$email = $_POST['email_address'];
$password = $_POST['password'];
$sql = "SELECT * FROM user where email = ? and password = ?";
$rs = $database->Execute($sql, array($email, md5($password)));
if ($rs->RecordCount() == 1) {
// valid user
$_SESSION['user'] = $rs->FetchRow();
}
Trong mã này, có một bước duy nhất chỉ truy xuất người dùng nếu - và chỉ khi - địa chỉ email và MD5 của mật khẩu văn bản đơn giản khớp với bản ghi cơ sở dữ liệu. Nếu chính xác một bản ghi được trả về, nó được gán cho phiên.
Để sử dụng password_verify (), chúng tôi cần một quy trình gồm hai bước:
- Truy xuất người dùng qua địa chỉ email
- Kiểm tra mật khẩu băm được lấy ra so với mật khẩu người dùng đã cung cấp
Bước 1
Bước đầu tiên, tôi có thể truy xuất người dùng bằng cách xóa kiểm tra mật khẩu khỏi truy vấn SQL:
$sql = "SELECT * FROM user where email = ?";
$rs = $database->Execute($sql, array($email));
if ($rs->RecordCount() == 1) {
// ..
Bước 2
Bây giờ tôi cần kiểm tra mật khẩu mà tôi làm với password_hash ():
if ($rs->RecordCount() == 1) {
$user = $rs->FetchRow();
$validPassword = password_verify($password, $user['password']);
if ($validPassword) {
// valid user
$_SESSION['user'] = $user;
}
}
Điều này hoạt động tuyệt vời cho tất cả người dùng có mật khẩu văn bản đơn giản được băm cập nhật, nhưng không ai trong số những người dùng hiện tại của tôi có thể đăng nhập! Điều này là do mật khẩu bcrypt của họ là hàm băm MD5 của mật khẩu văn bản đơn giản của họ.
Để cho phép tất cả người dùng đăng nhập, chúng tôi cũng cần kiểm tra hàm băm MD5 nếu password_verify () không thành công:
$validPassword = password_verify($password, $user['password']);
if (!$validPassword) {
// check for a legacy password
$validPassword = password_verify(md5($password), $user['password']);
}
if ($validPassword) {
// valid user
$_SESSION['user'] = $user;
Trong mã này, chúng tôi MD5 mật khẩu được cung cấp bởi người dùng và kiểm tra lại bằng password_verify đối với bản ghi cơ sở dữ liệu. Nếu lần này thành công, thì thông tin đăng nhập sẽ được xác minh.
Bây giờ tất cả người dùng của chúng tôi có thể đăng nhập thành công.
Di chuyển tại chỗ
Vì quá trình đăng nhập là lần duy nhất khi chúng tôi có sẵn mật khẩu văn bản đơn giản của người dùng, đây là thời điểm lý tưởng để di chuyển mật khẩu của người dùng trong cơ sở dữ liệu từ chuỗi MD5 được băm sang mật khẩu văn bản đơn giản được băm.
Tôi đã làm điều này trong mã nơi chúng tôi đã kiểm tra phiên bản MD5, nhưng chỉ khi kiểm tra thành công:
$validPassword = password_verify($password, $user['password']);
if (!$validPassword) {
// check for a legacy password
$validPassword = password_verify(md5($password), $user['password']);
if ($validPassword) {
// migrate user's record to bcrypt
$sql = 'UPDATE user SET password = ? WHERE id = ?';
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
$database->Execute($sql, [$hashedPassword, $user['id']]);
}
}
Bây giờ, mỗi khi người dùng đăng nhập bằng mật khẩu băm MD5, chúng tôi sẽ tự động băm lại mật khẩu văn bản đơn giản của họ thành bcrypt.
Cập nhật tạo mật khẩu
Cuối cùng, tôi đã duyệt và sửa tất cả mã tạo mật khẩu trong cơ sở dữ liệu. Đây là trong phần quản trị người dùng và các trang thay đổi mật khẩu và mật khẩu đặt lại của người dùng.
Trong mọi trường hợp, tôi đã thay đổi:
$password = md5($new_password);
đến
$password = password_hash($new_password, PASSWORD_DEFAULT);
password_hash () yêu cầu tham số thứ hai là thuật toán sử dụng. Trừ khi bạn có một lý do cụ thể để không, hãy sử dụng PASSWORD_DEFAULT.
Đó là nó
Đó là tất cả các bước mà tôi đã đi. Tôi hy vọng rằng đối với các ứng dụng được duy trì tích cực, hầu hết nếu không phải tất cả đã được cập nhật ngay bây giờ, vì PHP 5.5 đã xuất hiện vào năm 2009! Tuy nhiên, điều đó sẽ không làm tôi ngạc nhiên nếu có nhiều trang web được xây dựng bởi một cơ quan trong quá khứ nơi khách hàng không chủ động duy trì nó, mà chỉ yêu cầu cập nhật khi cần thay đổi - như trong trường hợp này.
Có thể bạn quan tâm
