Day25-從基礎學習 ThinkPHP-表單 Token

前言

講完表單驗證,那麼這一篇就要來講表單 Token,表單 Token 是需要搭配前面的表單驗證才能夠使用,所以我才會將表單 Token 留在表單驗證後面,那麼就讓我們來了解什麼是表單 Token 吧!

表單 Token 用途

首先表單 Token 最主要是為了解決 CSRF 的問題,CSRF 中文又稱為 跨站請求偽造,先前我其中一篇文章有稍微解釋過 CSRF,所以這邊就直接節錄部分內容。

CSRF 簡單來講就是利用使用者瀏覽器對於網站的信任來達到攻擊,假設我們已經知道某某某 API 路徑,例如刪除帳號的API https:/www.example.com/delete?id=XXX,然後黑客就傳給你一個句話「快看!點這個網址可以取得一個比特幣」(屁)

1
<a href="https:/www.example.com/delete?id=1">取得比特幣</a>

接下來你不疑有他就直接點了,當網站沒有做好 CSRF 防禦的時候,網站就只會知道說 「哦~你有我網站的 Cookie & Session,所以你要刪除 id = 1 的帳號是嗎?沒問題」就這樣網站不疑有他的把第一筆帳號刪除了,那麼為了避免這個狀況發生,那就要在網站上加入一個 CSRF 的防禦,當資料往後端傳送時,還必須帶上一組亂碼(類似 hash) 驗證若沒問題才會讓你執行,但是這一串數字並不是隨便帶都可以,而是要由瀏覽器觸發後端往前端傳才可以。

如果想要更詳細了解 CSRF 可以看這一篇文章

ThinkPHP 如何解決 CSRF

一般狀況下我們若是撰寫 Express.js 時,必須額外安裝套件,也就是 CSURF,但是在 ThinkPHP 中本身就有提供 CSRF 的解決方式,但是必須搭配表單驗證,所以我才會將表單 Token 放在這之後才介紹。

而使用方式非常簡單,只需要增加一個隱藏欄位即可:

1
<input type="hidden" name="__token__" value="{$Request.token}" />

接下來讓我們將這一段加入到註冊頁面,此時註冊頁面就會有一個隱藏欄位在表單上 ↓

token

當然你也可以偷懶一點只寫 {:token()},ThinkPHP 會幫我們自動在 HTML 上自動生成 hidden 欄位 ↓

token

接下來我們要增加提示訊息,所以要打開表單驗證的 validate/User.php,然後挑選任一個欄位,到時候它出現該錯誤訊息欄位的位子,例如我放在 Email 它就會出現在 Email 的錯誤訊息,所以我們來加入一段驗證看看:

1
2
3
4
5
protected $rule = [
'email' => 'require|email|token',
'password' => 'require|min:8|max:30',
'gender' => 'require|number',
];

這樣子就會驗證 token 了,但是這邊必須修改一下 Controller,否則你會發生連 token 都儲存進去資料庫的窘事,所以這邊必須使用 allowField(true) 的方法來解決這個問題:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function add() {
$user = new userModel;
$userData = input();
$result = $this->validate($userData, 'User');
if(true !== $result) {
Session::flash('error', $result);
$this->redirect('/user/signup');
}
if($user->allowField(true)->save($userData)){
return '新增成功';
} else {
return $user->getError();
}
}

當然你可能會發現有沒有加入 allowField(true) 感覺好像都沒差,但是如果你沒有在 Model 設置 field 的話,我個人建議要加上 allowField(true) 避免出現一些很奇怪的問題 (我個人也會建議加上)。

接下來讓我們來嘗試看看用 PostMan 打看看 http://localhost:8000/user/signup/add 吧。

token 無效

那麼這樣就可以成功防禦 CSRF 囉~但是你可能會說這個提示訊息能不能客製化,其實可以客製化訊息的,一樣打開 validate/User.php 加入一段客製化訊息在 protected $message 即可:

1
2
3
4
5
6
7
8
9
10
protected $message = [
'email.require' => 'Email 必填',
'email.email'=> 'Email 格式錯誤',
'email.token' => 'Token 出現問題請重新整理。', // Token 錯誤訊息
'password.require' => '密碼欄位必填',
'password.min' => '密碼最短長度為 8',
'password.max' => '密碼長度最長為 30',
'gender.require' => '性別必填',
'gender' => '性別必須為是數字'
];

message

接下來我們再打一次就可以看到提示訊息變了 ↓

Token 出現問題請重新整理。

結尾

大致上已經將驗證方面的東西介紹差不多了,如果想了解更詳細的驗證以及如何客製化自己的驗證器,那麼我會建議看官方手冊的驗證章節會更詳細 → 官方手冊

下一篇將會來介紹驗證碼,驗證碼最常使用於登入了~