前言
本文為《現代 PHP》一書的學習筆記。
環境
密碼
密碼安全性非常重要,安全的管理、雜湊並儲存使用者密碼是開發者的職責之一。幸運的是,PHP 提供了內建的工具讓密碼安全性管理變得簡單許多。
絕對不要知道使用者密碼
開發者永遠不應該有辦法得知使用者的密碼,如果應用程式的資料庫被入侵,將帶來成堆的法律問題,名譽也將受損。
絕對不要限制使用者密碼
如果要求使用者地密碼符合特定格式,等同於提供一個好用的指南讓惡意駭客侵入應用程式。如果非得限制使用者的密碼,最好就是限制最短長度。
絕對不要以電子郵件寄送使用者密碼
如果藉由電子郵件寄送使用者的密碼,使用者將會知道他的密碼可能是不安全的,而且是可以被知道的。
相反,開發者應該使用電子郵件發送一個 URL,讓使用者在按下「忘記密碼」的連結後,被導向一個可以重新設定密碼的表單頁面。
使用 bcrypt 雜湊
確保使用者的密碼被雜湊而不是被加密。「加密」與「雜湊」並非同義字,前者是雙向的演算法,被加密的資料可以被解密;而後者是單向的,被雜湊的資料不能被倒回為原本的形式,而相同的資料永遠會產生出相同的雜湊碼。
當使用者密碼被雜湊後儲存於資料庫,入侵的駭客只會看到無意義的密碼雜湊碼,需要花費大量的時間和資源來破解。許多雜湊演算法都是開放使用的(例如 MD5
、SHA1
、bcrypt
、scrypt
),而速度與安全性是使用時需要考慮的要素。
現今被檢驗最安全的雜湊演算法是 bcrypt
,它被設計成十分緩慢,但最終的雜湊碼非常的安全。雜湊疊代的次數稱為「工作係數」(work factor),工作係數和雜湊碼被破解的難度呈指數性上升幅度。
密碼雜湊 API
PHP 原生的密碼雜湊 API 提供了易用的函式,簡化了密碼雜湊的程序,預設是使用 bcrypt
雜湊演算法。
使用者註冊
假想一個應用程式接收到一個 HTTP
的 POST
請求,夾帶著有效的電子郵件以及至少八個字元的密碼,就允許建立一個使用者密碼。
範例 5-7:使用者註冊腳本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| try { $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL); if (!$email) { throw new Exception('Invalid email'); }
$password = filter_input(INPUT_POST, 'password'); if (!$password || mb_strlen($password) < 8) { throw new Exception('Password must contain 8+ characters'); }
$passwordHash = password_hash( $password, PASSWORD_DEFAULT, ['cost' => 12] ); if ($passwordHash === false) { throw new Exception('Password hash failed'); }
header('HTTP/1.1 302 Redirect'); header('Location: /login.php'); } catch (Exception $e) { header('HTTP/1.1 400 Bad request'); echo $e->getMessage(); }
|
使用者登入
假想一個應用程式接收到一個 HTTP
的 POST
請求,夾帶著電子郵件以及密碼,用來認證登入使用者。
範例 5-7:使用者登入腳本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| session_start(); try { $email = filter_input(INPUT_POST, 'email');
$password = filter_input(INPUT_POST, 'password');
$user = User::findByEmail($email);
if (password_verify($password, $user->password_hash) === false) { throw new Exception('Invalid password'); }
$currentHashAlgorithm = PASSWORD_DEFAULT; $currentHashOptions = array('cost' => 15); $passwordNeedsRehash = password_needs_rehash( $user->password_hash, $currentHashAlgorithm, $currentHashOptions ); if ($passwordNeedsRehash === true) { $user->password_hash = password_hash( $password, $currentHashAlgorithm, $currentHashOptions ); $user->save(); }
$_SESSION['user_logged_in'] = 'yes'; $_SESSION['user_email'] = $email;
header('HTTP/1.1 302 Redirect'); header('Location: /user-profile.php'); } catch (Exception $e) { header('HTTP/1.1 401 Unauthorized'); echo $e->getMessage(); }
|
password_needs_rehash()
函式用來檢驗密碼雜湊値是否和指定的選項匹配。
password_verify()
函式用來驗證密碼是否和密碼雜湊値匹配。
重新雜湊密碼
為什麼應當建立一個新的密碼雜湊値?假設應用程式是兩年前建立的,當時使用的 bcrypt
工作係數是 10
,而現在是使用 20
,因此需要確保密碼雜湊値被更新。
參考資料
- Josh Lockhart(2015)。現代 PHP。台北市:碁峯資訊。