# 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"); // 沒有登入成功的話顯示登入失敗
}
```
- 這可以保護使用者密碼,這很重要,千萬不能在資料庫內存明碼。
***