# Week11 7 、 明文密碼 ###### tags: `BE101` `PHP` `2020十一月第二周` `進度筆記` `Lidemy心得` *** 資料加密: 在資料庫內把 user 的密碼儲存後,存明碼後容易被竊取,因此要避免直接把密碼存在資料庫上,有很多網站要求重設密碼,其實網站本身也不知道 user 密碼。 *** # Week11 7-1 、 加密與 hash 區別 ###### tags: `BE101` `PHP` `2020十一月第二周` `進度筆記` `Lidemy心得` *** ## 編碼(Encoding) - 把一團資料各自表述,使用不同方法來解釋,不用金鑰就能解碼的不安全方式。 ## 加密(Encryption) - 一對一的方式。 - 用金鑰來保護重要資料的安全方法,且加密和解密過程都需要用到金鑰。 - 如果被知道加密演算法,和金鑰,那被存在資料庫的密碼都會被解開。 ``` 明文(ex. aaa) → 加密 → 密文(ex. bbb) 密文(ex. bbb) → 解密 → 明文(ex. aaa) ``` ## 雜湊(Hash) - 多對一的方式,會有很多種字串對應到有限的結果。 - 把一堆欄位的資料丟進某種 function (Hash function)進行運算,且得到的結果無法用來反推回原來的資料。 ``` 明文(ex. aaa) → Hash → 文字(ex. j1o1h7n) ``` - 如果有兩不同東西產生同樣 Hash 的結果時被稱之為"碰撞"。 - 知名的演算法發生碰撞機率超級低,幾乎不太可能。 ## 例如,取餘數 ``` n%999 1 => hash =>1 2 => hash =>2 但 1001 => hash => 2 2000 => hash => 2 ``` 知道結果是 2 , 但是無法知道 hash 前輸入的值是多少,因此是單向的。 - sha256 > MD5(不安全) 。 - https://emn178.github.io/online-tools/sha256.html - 使用者在註冊的時候,把其密碼經過 hash 後存到資料庫。 - 其 user 在登入時輸入的密碼一樣也透過 hash 後,與資料庫經 hash 的 user 密碼做比對,如果出來的結果一樣,代表輸入的值一樣,因此可以應用。 *** 參考資料: [\[資訊安全\] 密碼存明碼,怎麼不直接去裸奔算了?淺談 Hash , 用雜湊保護密碼](https://medium.com/@brad61517/%E8%B3%87%E8%A8%8A%E5%AE%89%E5%85%A8-%E5%AF%86%E7%A2%BC%E5%AD%98%E6%98%8E%E7%A2%BC-%E6%80%8E%E9%BA%BC%E4%B8%8D%E7%9B%B4%E6%8E%A5%E5%8E%BB%E8%A3%B8%E5%A5%94%E7%AE%97%E4%BA%86-%E6%B7%BA%E8%AB%87-hash-%E7%94%A8%E9%9B%9C%E6%B9%8A%E4%BF%9D%E8%AD%B7%E5%AF%86%E7%A2%BC-d561ad2a7d84) [一次搞懂密碼學中的三兄弟 — Encode、Encrypt 跟 Hash](https://medium.com/starbugs/what-are-encoding-encrypt-and-hashing-4b03d40e7b0c) [帳戶密碼的加密與雜湊 \- 翻轉工作室](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwim5camzrHtAhWZKqYKHcAYBhsQFjAJegQIChAC&url=http%3A%2F%2Fwww.tsnien.idv.tw%2FSecurity_WebBook%2Fchap13%2F13-3%2520%25E5%25B8%25B3%25E6%2588%25B6%25E5%25AF%2586%25E7%25A2%25BC%25E5%258A%25A0%25E5%25AF%2586%25E8%2588%2587%25E9%259B%259C%25E6%25B9%258A.html&usg=AOvVaw3kylZj_7pCDeiss_Ph6t2Z) [加密和雜湊有什麼不一樣? | Just for noting](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwim5camzrHtAhWZKqYKHcAYBhsQFjAAegQIBRAC&url=https%3A%2F%2Fblog.m157q.tw%2Fposts%2F2017%2F12%2F25%2Fdifferences-between-encryption-and-hashing%2F&usg=AOvVaw1xPGuTJXQKAkSvniNuFwiQ) [\[淺談\] 編碼(encoding) vs 加解密vs 雜湊(Hash) | 石頭的coding ...](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwim5camzrHtAhWZKqYKHcAYBhsQFjAEegQIBBAC&url=https%3A%2F%2Fdotblogs.com.tw%2Fdaniel%2F2019%2F05%2F06%2F223004&usg=AOvVaw3yPB4lVFHPoqlANRIbRqFw) *** # Week11 7-2 、 使用 PHP 內建 hash 函式 ###### tags: `BE101` `PHP` `2020十一月第二周` `進度筆記` `Lidemy心得` *** - 用 PHP 內建函式 `password_hash()` 。 - 有些舊的版本不支援(5.5.0 以上版本) 。 - 現在回傳的字串是 60 字元,但以後可能會變長,建議預留 255 的空間。 *** ## 註冊處理頁面 handle_register.php 檔案: ``` <?php session_start(); require_once('conn.php'); if ( empty($_POST['nickname']) || empty($_POST['username']) || empty($_POST['password']) ) { header('Location: register.php?errCode=1'); die(); } $nickname = $_POST['nickname']; $username = $_POST['username']; $password = password_hash($_POST['password'], PASSWORD_DEFAULT); // 註冊時,密碼存到資料庫時就會是經過 hash 的結果 $sql = sprintf( "insert into users(nickname, username, password) values('%s', '%s', '%s')", $nickname, $username, $password ); $result = $conn->query($sql); if (!$result) { $code = $conn->errno; if ($code === 1062) { header('Location: register.php?errCode=2'); } die($conn->error); } $_SESSION['username'] = $username; header("Location: index.php"); ?> ``` - 會把經過 hash 演算的字串結果存到資料庫。 - 會存經過 hash 演算法的相關資訊、到這個字串。 *** ## 註冊登入頁面 - 使用 `password_verify()` 去驗證登入時的 hash 與存在資料庫的 hash 結果是否相同。 handle_login.php 檔案: ``` <?php session_start(); require_once('conn.php'); require_once('utils.php'); if ( empty($_POST['username']) || empty($_POST['password']) ) { header('Location: login.php?errCode=1'); die(); } $username = $_POST['username']; $password = $_POST['password']; $sql = sprintf( "select * from users where username='%s'", $username ); // 原本是看帳號密碼是否相同,但經 hash 前要先把密碼 select 出來 $result = $conn->query($sql); if (!$result) { die($conn->error); } if ($result->num_rows === 0) { header("Location: login.php?errCode=2"); exit(); // 如果沒有東西就是查無 user , 先加 exit 可以避免在資料庫查詢前先執行下面比對 hash 的動作 } // 有查到使用者 $row = $result->fetch_assoc(); //拿出結果 if (password_verify($password, $row['password'])) { // 第一個是登入成功($password),第二個是資料庫的($row['password']),比對兩者 /* 1. 產生 session id (token) 2. 把 username 寫入檔案 3. set-cookie: session-id */ $_SESSION['username'] = $username; // 指定註冊後直接成為登入狀態 header("Location: index.php"); } else { header("Location: login.php?errCode=2"); // 沒有登入成功的話顯示登入失敗 } ?> ``` - 註冊時,密碼存到資料庫時就會是經過 hash 的結果。 ``` $password = password_hash($_POST['password'], PASSWORD_DEFAULT); ``` - 登入時,先透過 username 把 密碼挖出來。 ``` $sql = sprintf( "select * from users where username='%s'", $username ); ``` - 並且要先檢查有沒有 user , 這個使用者存在與否,沒有的話就錯誤返回。 ``` if ($result->num_rows === 0) { header("Location: login.php?errCode=2"); exit(); } ``` - 如果有查到使用者。 ``` $row = $result->fetch_assoc(); //拿出結果 if (password_verify($password, $row['password'])) { // 第一個是登入成功($password),第二個是資料庫的($row['password']),比對兩者 /* 1. 產生 session id (token) 2. 把 username 寫入檔案 3. set-cookie: session-id */ $_SESSION['username'] = $username; // 指定註冊後直接成為登入狀態 header("Location: index.php"); } else { header("Location: login.php?errCode=2"); // 沒有登入成功的話顯示登入失敗 } ``` - 這可以保護使用者密碼,這很重要,千萬不能在資料庫內存明碼。 ***