安全相關(guān)對于一個框架來說,安全體系是非常重要的一環(huán)。如果一個框架沒有好的安全措施及功能的話,那么這個框架在線上運(yùn)行的時候多多少少還是會讓人不放心的,畢竟各路大佬可能隨時都在掃描各個網(wǎng)站的漏洞。之前的各種安全事件可能你不一定經(jīng)歷過,但一定聽說過。今天,我們就來看看 Laravel 中的安全相關(guān)功能。 認(rèn)證體系在 Laravel 中,自帶了一套用戶登錄認(rèn)證體系,這一套體系原來是直接框架自帶的,現(xiàn)在剝離出來通過 laravel/jetstream 組件實(shí)現(xiàn)了。默認(rèn)情況下,我們安裝 Laravel 框架后,會自帶一個默認(rèn)的 User Model ,這個 Model 就是這個默認(rèn)用戶表的模型類。
通過這三個命令行代碼,我們就可以安裝好 Jetstream 相關(guān)的組件,安裝完成后,將會自帶路由以及 view 界面,我們可以訪問 /register 路徑,返回的界面是這個樣子的。
在這個文件中,我們還可以看到 login、logout、user 相關(guān)的操作路由。那么它的模板在哪里呢?其實(shí)上面的 npm run dev 操作就是編譯了 Laravel 框架自帶的 Vue 框架,而模板走的正是 Vue ,文件在 resource/js/Pages 中,在這里我們可以找到 Auth/Register.vue 這個文件,隨便修改一點(diǎn)然后再次執(zhí)行 npm run dev 重新編譯,就可以看到修改之后的內(nèi)容了。或許還有別的方法,可以走普通的直接輸出的頁面形式,因?yàn)?view/auth 下面也生成了一些文件,一開始我還以為是走的這里的前端文件,但結(jié)果并不是。我們也不深究了。 費(fèi)勁嗎?其實(shí)挺費(fèi)勁的,如果是正式的公司團(tuán)隊(duì)開發(fā)的話,前端小哥哥小姐姐們才不會來你的 Laravel 框架中進(jìn)行編譯或者寫代碼呢。所以這個功能更適合的是我們自己一個人承擔(dān)一整套后臺頁面開發(fā)的情況。說白了,做私活的時候很方便。 不過,更多情況下其實(shí)我們還是寧愿自己使用 vue 腳手架去讓前后端完全分離,所以這一塊的功能,大家了解一下就好。接下來我們看看怎么自己實(shí)現(xiàn)這些注冊登錄操作,以接口形式。(網(wǎng)頁形式也是同理的) 自已實(shí)現(xiàn)的注冊、登錄要自己實(shí)現(xiàn)登錄注冊其實(shí)非常簡單,如果只是網(wǎng)頁的登錄,同樣我們還是使用 Laravel 自帶的那個 users 數(shù)據(jù)表,然后自定義幾個路由和控制器。
在這個控制器中,我們在 Login 方法中使用了 attempt() 方法來實(shí)現(xiàn)登錄功能,只需要將原始的用戶名和密碼傳遞進(jìn)去,方法內(nèi)部會查詢用戶并進(jìn)行比對,它默認(rèn)走的是 User 這個 Model ,調(diào)用的數(shù)據(jù)表就是 users 表。登錄成功后會直接種下 Session 和 Cookie ,大家可以自行查看請求返回的 Cookie 信息以及查找你系統(tǒng)保存的 Session 數(shù)據(jù)。
在路由中,我們給最后的這個 info 添加了一個中間件,如果請求它的時候沒有 Cookie 信息,那么它就會返回 403 未認(rèn)證的信息。大家可以自己嘗試一下,接下來我們要看一下如何使用 token 來進(jìn)行 api 的登錄和認(rèn)證控制。一般情況下,我們可能會使用 jwt 或者 passport 之類的插件來做這種 api 的認(rèn)證功能。不過這些內(nèi)容不在我們今天討論的范圍內(nèi),我們只是看一下默認(rèn)情況下 Laravel 自帶的認(rèn)證是如何使用的。 默認(rèn)情況下,Laravel 框架雖然提供了 Api 的驗(yàn)證功能,但還需要我們手動的添加一些內(nèi)容,比如說數(shù)據(jù)庫需要添加一個 api_token 的 varchar 字段,給個 80 左右的長度即可。然后我們改造一下登錄和路由驗(yàn)證中間件。
在登錄這塊,我們只需要在每次登錄的時候去新建一個 api_token 并保存到數(shù)據(jù)中就可以了。然后將生成的這個 api_token 返回交給前端保存。
接著將 info 這個測試接口的中間件換成框架自帶的 auth:api 就可以了。接下來你可以自己測試一下效果,在訪問 /custom/info 這個接口時,你可以用兩種方式來傳遞 api_token 。
中間件守護(hù)在 Laravel 的認(rèn)證體系中,中間件有守衛(wèi)的職責(zé),包括在配置文件和 Auth 的常用方法中都有 guard 這個單詞的出現(xiàn)。我們在源碼中主要就來看一下它的中間件是如何進(jìn)行認(rèn)證守護(hù)的。 框架中的 app\Middleware\Authenticate 繼承自 vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php 方法,其中 handle() 方法最終調(diào)用的是 authenticate() 方法。
這個方法內(nèi)部會調(diào)用 auth 對象的 grard() 方法并鏈?zhǔn)嚼^續(xù)調(diào)用 check() 方法來判斷用戶是否登錄。這個 auth 對象實(shí)際上是 vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php 對象。我們可以進(jìn)入這個類模板中查看 guard() 方法。
在創(chuàng)建驅(qū)動時,會根據(jù)我們在 config/auth.php 中的配置,調(diào)用指定的驅(qū)動,比如 web 調(diào)用的是 SessionGuard ,而 api 則會調(diào)用 TokenGuard ,這里的驅(qū)動生成和我們之前看過的緩存驅(qū)動非常類似,大家可以自己看一下,最后其實(shí)就是通過配置文件的內(nèi)容拼接成類名并獲得實(shí)例化對象。我們繼續(xù)以 TokenGuard 為例往下看。
vendor/laravel/framework/src/Illuminate/Auth/TokenGuard.php 的構(gòu)造函數(shù)默認(rèn)指定的 key 就是 api_token ,這下明白為什么我們在數(shù)據(jù)庫添加的字段必須是 api_token 這個字段了吧。check() 方法在 TokenGuard 所使用的那個 GuardHelpers 特性對象中,它會再調(diào)用 user() 方法。
在這個方法內(nèi)部,又會調(diào)用 getTokenForRequest() 來獲得請求參數(shù)中的 api_token 參數(shù),如果不存在的話,則會使用 request 的 bearerToekn() 方法來獲得在頭信息中的 Authorization 數(shù)據(jù)。這也是我們使用 api 方式可以用兩種方式傳遞 token 的原因。最后,通過獲得的 token 調(diào)用 UserProvider 服務(wù)提供者獲得用戶信息完成登錄認(rèn)證的判斷。整個認(rèn)證守衛(wèi)的過程就完成了。 加密解密對于加密來說, Laravel 框架直接使用的就是 OpenSSL 提供的 AES-256 和 AES-128 加密。也就是說,這個默認(rèn)的加密功能使用的是 對稱加密 的形式。在之前我們已經(jīng)學(xué)習(xí)過 PHP 中的加密以及 OpenSSL 的加密,對稱加密是需要一個密鑰的,這個密鑰其實(shí)就是我們在安裝框架之后使用 php artisan key:generate 生成的那個密鑰,它被保存在 .env 文件中。這個命令是我們最開始第一篇文章搭建 Laravel 框架時就見過的。 所有 Laravel 加密之后的結(jié)果都會使用消息認(rèn)證碼 (MAC) 簽名,使其底層值不能在加密后再次修改。因此,最好建議是使用 Laravel 內(nèi)建的加密工具。
測試代碼很簡單,也沒什么多說的,主要就是 encrypt() 加密和 decrypt() 解密這兩個函數(shù)。它們的實(shí)現(xiàn)在 vendor/laravel/framework/src/Illuminate/Encryption/Encrypter.php 中,具體如何通過門面找到這個實(shí)現(xiàn)類想必也不用我多說了。具體實(shí)現(xiàn)的內(nèi)容大家可以自己去看這兩個方法,如果有疑問,可以查看之前我們學(xué)習(xí)過的 PHP的OpenSSL加密擴(kuò)展學(xué)習(xí)(一):對稱加密https://mp.weixin.qq.com/s/mkUjW5MFQyJDFS4YyMdyaA 。 哈希和上面的 Crypt 加密一樣,Hash 門面使用的其實(shí)就是 password_hash() 的加密方式,Laravel 也只是對它進(jìn)行了一個簡單的封裝。
關(guān)于測試結(jié)果和參數(shù)我也不多說了,源碼大家也自己去翻一翻吧,非常好找,make() 底層就是 password_hash() ,check() 底層就是 password_verify() ,而 needsRehash() 底層就是 password_needs_rehash() 。對于 password_hash() 有疑問的同學(xué)也可以移步我們之前學(xué)習(xí)過的 PHP密碼散列算法的學(xué)習(xí) https://mp.weixin.qq.com/s/d_qI3GKB-DoNrBNb7r_LaA 再好好復(fù)習(xí)一下。 防注入對于注入來說,我們最關(guān)心的無非就是兩種注入問題,一個是 SQL 注入,一個是 XSS 注入。對于 SQL 注入,只要你使用框架的 查詢構(gòu)造器 或者 模型 ?;静粫刑蟮淖⑷雴栴}。當(dāng)然,前提是不要直接去用 DB::select() 這樣的寫自己拼的 SQL 語句。 而對于 XSS 來說呢?雖然在模板輸出的時候已經(jīng)默認(rèn)做了一些安全防護(hù)的操作,但我們接收到的參數(shù)如果入庫了,可能會有存儲型 XSS 的潛在風(fēng)險(xiǎn)。這個東西框架沒有提供直接的解決功能,大家可以使用 HtmlPurifier 來解決,直接 Composer 安裝就可以了。 總結(jié)今天的內(nèi)容主要是探討了一下 Laravel 框架中自帶的認(rèn)證功能和加密相關(guān)的內(nèi)容。其實(shí)更多情況下,我們會自己去做 api 形式的接口或者自己去寫登錄頁面和驗(yàn)證的邏輯。畢竟對于大多數(shù)項(xiàng)目來說,用戶表的情況可能并不和框架所提供的完全一樣,可能很多字段也不相同。不過原始的認(rèn)證模塊還是非常好用的,大家可以多多嘗試。至于加密相關(guān)的和注入安全相關(guān)的知識可以查閱我們之前的文章以及自己去搜索相關(guān)的資料。這些東西都是通用的,框架能做的其實(shí)也就這么多。 參考文檔: https:///docs/laravel/8.5/authentication/10397 |
|