目前絕大多數的系統都少不了登入驗證的功能,這主要是為了保存使用者的狀態,以此來限制使用者的各種行為,從而方便有效的控制用戶的權限。例如一個使用者登陸微博,發布、追蹤、評論的操作都應是在登入後的使用者狀態下進行的。
實作登入驗證的功能主要有Cookie&Session
、JWT
兩種方式,這一節我們將先對Cookie&Session
的工作原理 做詳細的介紹,在之後的文章中會陸續對JWT
,以及如何使用Cookie&Session
和JWT
來完善前幾節我們建置的簡易用戶管理系統進行講解。 【相關教學推薦:nodejs影片教學】
我們知道,HTTP 是無狀態的。也就是說,HTTP 請求方和回應方間無法維護狀態,都是一次性的,它不知道前後的請求都發生了什麼事。但有的場景下,我們需要維護狀態。最典型的,一個使用者登陸微博,發佈、追蹤、評論,都應是在登入後的使用者狀態下的。
這個時候就可以引入Cookie
與Session
來儲存使用者的登入狀態。
本篇文章主要介紹使用
Cookie-Session
來做登入驗證的工作原理,關於Cookie
#與Session
的詳細介紹可查閱這位大佬的文章:Cookie和Session詳解
Cookie
是存放在瀏覽器中的,可以在瀏覽器中開啟控制台
,選擇應用程式
,找到儲存
中的Cookie
進行檢視:
#當客戶端向服務端發送網路請求時瀏覽器會自動將Cookie
加入到請求頭中,這樣服務端就能取得這個Cookie
,如下:
知道了這個原理後,我們就可以想到,如果在使用者登入系統時:客戶端由使用者的部分登入資訊(例如username
、id
等)產生一個Cookie
存放到瀏覽器中,那麼在這之後的每一次網路請求都會自動攜帶上該Cookie
。
之後讓服務端根據請求中是否攜帶Cookie
並且攜帶的Cookie
中是否存在有效的username
、id
來判斷使用者是否已經登入過了,這樣一來使用者的登入狀態不就被保存下來了嗎。
回到上面我們提到的微博的例子,按照這個過程來說,當使用者登入後Cookie
已經被儲存,這時當使用者進行發佈、追蹤、評論等需要登入才能使用的操作時我們就能提前判斷是否存在Cookie
,如果存在且Cookie
中含有該使用者的id
,那麼我們就可以允許該使用者的這些操作(這些操作一般都是需要使用者的id
的,這時就可以從Cookie
中進行取得)。相反的,如果Cookie
不存在或Cookie
#無效,那麼就禁止該使用者的這些操作。
說到這,你可能會問:既然一個Cookie
就能實現我們想要的效果,那為何還要使用Session
呢?
這是因為 Cookie
很容易被偽造! ,如果我們知道了Cookie
中存放的資訊是username
和id
(就算不知道,也可以登入後的網路請求的請求體中找到Cookie
),那麼我們完全可以在不登入的情況下手動向瀏覽器儲存一個偽造的Cookie
:
說到這,你應該就能明白為什麼不能單獨使用Cookie
了吧。
Session
其實是基於Cookie
實現的,並且Session
儲存在服務端的記憶體或資料庫中。
當使用者登入成功時,使用Cookie&Session
的登入驗證會進行以下操作:
由服務端產生Session
與SessionId
;
Session
一般是根據使用者登入的信息,如使用者名稱、id
等進行生成。
如果把Session
比喻為是一把鎖,那麼SessionId
就等於是這把鎖的鑰匙。
服務端將Session
儲存到記憶體或資料庫中;
服務端將 SessionId
存放到請求的回應頭(response
物件)中的Set-Cookie
欄位中傳送給客戶端;
客戶端收到Set-Cookie
後會自動將Set-Cookie
的值(也就是SessionId
)存放到Cookie
中;
之後的每個網路請求都會自動帶上Cookie
,也就是帶上這個SessionId
;
服務端收到後續請求時取得請求上的Cookie
,也就是取得到了SessionId
,然後透過SessionId
#查詢並校驗服務端儲存的Session
,若校驗成功說明這個SessionId
有效則透過此請求,反之則阻止此請求。
圖示:
為了保存使用者的登入狀態,我們需要為每位登入的使用者產生並儲存Session
,這勢必就會造成以下問題:
Session
存放到記憶體中,那麼當服務端重啟時,這些記憶體中的Session
都會被清除,那麼所有使用者的登入狀態都會過期,當使用者量較大時,過多的記憶體佔用也勢必會影響服務端的效能。 Session
存放到資料庫中,雖然能夠解決因服務端重啟造成用戶登入狀態過期的問題,但當用戶量較大時,對於這個資料庫的維護也會改變得相對困難。 Session
在兩個伺服器間共用通常會將Session
存放到一個單獨的資料庫中,這樣就使得整個專案變得更為複雜也更難維護。 CSRF 全稱為Cross-site request forgery 即跨站請求偽造,使用Cookie
進行驗證的網站都會面臨或大或小的CSRF
威脅,我們以一個銀行網站的例子來介紹CSRF的攻擊原則:
假如一家銀行網站A
的登入驗證採用的是Cookie&Session
,並且該網站上用以執行轉帳作業的Api位址
為:http://www.grillbankapi.com/?account=AccoutName&amount=1000
api
參數:account
代表帳戶名,amount
代表轉帳金額。
那麼,一個惡意攻擊者可以在另一個網站B
上放置以下程式碼:
<img alt="Node學習之聊Cookie-Session登入驗證的工作原理" >
注意:
img
標籤的src
是網站A
轉帳操作的api位址
,且參數account
為Ailjx,amount
為1000 ,也就是說這個api位址
相當於是帳戶名稱名為Ailjx 轉帳1000 時調用的api
。
如果有帳號名稱Ailjx 的使用者剛造訪過網站A
不久,登入資訊尚未過期(網站A
的Cookie
存在且有效)。
那麼當Ailjx 訪問了這個惡意網站B
時,上面的img
標籤將被加載,瀏覽器就會自動請求img
標籤的src
路由,也就是請求http://www.grillbankapi.com/?account=Ailjx&amount=1000
(我們將這個請求記為請求Q
),並且因為Cookie
存放在瀏覽器中且瀏覽器發送請求時會自動帶上Cookie
,所以請求Q
上就會自動攜帶Ailjx 在網站A
上的Cookie
憑證,結果就是這個請求Q
將會被通過,那麼Ailjx 就會損失1000資金。
這種惡意的網址可以有很多種形式,藏身於網頁中的許多地方。 此外,攻擊者也不需要控制放置惡意網址的網站。例如他可以將這種地址藏在論壇,部落格等任何用戶生成內容的網站中。這意味著如果服務端沒有合適的防禦措施的話,使用者即使訪問熟悉的可信任網站也有受攻擊的危險。
透過例子能夠看出,攻擊者並不能透過CSRF攻擊來直接取得使用者的帳戶控制權,也無法直接竊取使用者的任何資訊。他們能做到的,是欺騙用戶瀏覽器,讓其以用戶的名義運行操作。
這些就是使用Cookie&Session
來做登入驗證的問題所在,那麼我們要如何解決這些問題呢?這就需要引進JWT的概念,使用token
來做登入驗證,這些我們將在之後的文章中進行解說。
更多node相關知識,請造訪:nodejs 教學!
以上是Node學習之聊Cookie-Session登入驗證的工作原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!