目前全文是翻譯的,講的主要是 cookie 驗(yàn)證和 token 驗(yàn)證的區(qū)別,cookie 驗(yàn)證準(zhǔn)確的說是利用 cookie 來作為媒介艺谆,存儲 session ID 進(jìn)行驗(yàn)證,token 其實(shí)也可以借助 cookie 來存儲仅政,不過下文中提到的 cookie 驗(yàn)證主要是指 session ID 存儲到 cookie 中進(jìn)行的驗(yàn)證垢油。token 驗(yàn)證主要是指 token 存儲在 Authorization Header 中進(jìn)行的驗(yàn)證。
這個翻譯并不是完全按照原本逐字意譯圆丹,有的是我看了原本以后根據(jù)自己的理解寫了出來滩愁,同時省略了一些我認(rèn)為不重要的內(nèi)容,加了一點(diǎn)我自己的筆記和疑問的地方辫封。
原文地址:https://auth0.com/blog/cookies-vs-tokens-definitive-guide/
下面是譯文:
Cookie
cookie 驗(yàn)證是用于長時間用戶驗(yàn)證硝枉,cookie 驗(yàn)證是有狀態(tài)的,意味著驗(yàn)證記錄或者會話需要一直在服務(wù)端和客戶端保持倦微。服務(wù)器需要保持對數(shù)據(jù)庫活動會話的追蹤妻味,當(dāng)在前端創(chuàng)建了一個 cookie,cookie 中包含了一個 session 標(biāo)識符欣福。傳統(tǒng) cookie 會話的驗(yàn)證流程:
用戶登錄责球,輸入賬號密碼
服務(wù)器驗(yàn)證用戶賬號密碼正確,創(chuàng)建一個 session 存儲在數(shù)據(jù)庫(或者 redis)
將 session ID 放進(jìn) cookie 中拓劝,被存儲在用戶瀏覽器中雏逾。
再次發(fā)起請求,服務(wù)器直接通過 session ID 對用戶進(jìn)行驗(yàn)證
一旦用戶登出郑临,則 session 在客戶端和服務(wù)器端都被銷毀
Token
token 驗(yàn)證是無狀態(tài)的栖博,服務(wù)器不記錄哪些用戶登錄了或者哪些 JWT 被發(fā)布了,而是每個請求都帶上了服務(wù)器需要驗(yàn)證的 token厢洞,token 放在了 Authorization header 中仇让,形式是 Bearer { JWT },但是也可以在 post body 里發(fā)送犀变,甚至作為 query parameter妹孙。
驗(yàn)證流程:
用戶輸入登錄信息
服務(wù)器判斷登錄信息正確,返回一個 token
token 存儲在客戶端获枝,大多數(shù)通常在 local storage蠢正,但是也可以存儲在 session storage 或者 cookie 中。
接著發(fā)起請求的時候?qū)?token 放進(jìn) Authorization header省店,或者同樣可以通過上面的方式嚣崭。
服務(wù)器端解碼 JWT 然后驗(yàn)證 token笨触,如果 token 有效,則處理該請求雹舀。
一旦用戶登出芦劣,token 在客戶端被銷毀,不需要經(jīng)過服務(wù)器端说榆。
Token 驗(yàn)證的優(yōu)勢
無狀態(tài)虚吟,可擴(kuò)展和解耦
使用 token 而不是 cookie 的最大優(yōu)點(diǎn)應(yīng)該就是無狀態(tài),后端不需要保持對 token 的記錄签财,每個 token 都是獨(dú)立的串慰,包含了檢查其有效性的所有數(shù)據(jù),并通過申明傳達(dá)了用戶信息唱蒸。
服務(wù)器端的工作只需要在登錄成功后邦鲫,生成(或者 sign,簽署) token神汹,或者驗(yàn)證傳入的 token 是否有效庆捺。有時候甚至不需要生成 token,第三方服務(wù)比如 Auth0 可以處理 token 的簽發(fā)屁魏,服務(wù)器只需要驗(yàn)證 token 的有效性就可以滔以。跨域和 CORS
cookie 能很好的處理單域和子域,但是遇到跨域的問題就會變得難以處理蚁堤。而使用 token 的 CORS 可以很好的處理跨域的問題醉者。由于每次發(fā)送請求到后端,都需要檢查 JWT披诗,只要它們被驗(yàn)證通過就可以處理請求撬即。在 JWT 中存儲數(shù)據(jù)
當(dāng)使用 cookie 進(jìn)行驗(yàn)證時,你是將 session id 存儲到 cookie 里呈队,JWT 允許你存儲任何類型的元數(shù)據(jù)剥槐,只要是合法的 JSON。你可以在里面添加任何數(shù)據(jù)宪摧,可以只有用戶 ID 和到期日粒竖,也可以添加其它的比如郵件地址,域名等等几于。
比如:加入你有一個 API 是 /api/orders 蕊苗,用于取回最新的訂單,但是只有 admin 角色的用戶可以獲取到這些數(shù)據(jù)沿彭。在基于 cookie 的驗(yàn)證中朽砰,一旦請求被創(chuàng)建,就需要先去訪問數(shù)據(jù)庫去驗(yàn)證 session 是否正確(現(xiàn)在應(yīng)該都是存儲到 redis 里了,不會存數(shù)據(jù)庫里了)瞧柔,另外還要去獲取數(shù)據(jù)庫里的用戶權(quán)限去校驗(yàn)用戶是否擁有 admin 的權(quán)限(這個應(yīng)該是根據(jù)用戶 role_id 查看權(quán)限是否是 admin)漆弄,最后才是調(diào)用訂單信息。而使用 JWT 的話造锅,可以將用戶角色放進(jìn) JWT 內(nèi)撼唾,所以只要驗(yàn)證通過了,就可以直接調(diào)用訂單信息哥蔚。移動平臺
現(xiàn)代的 API 不僅僅和瀏覽器交互倒谷,正確編寫一個 API 可以同時支持瀏覽器,還有原生移動平臺糙箍,比如 IOS 或者 Android恨锚。原生移動平臺并不一定和 cookie 能良好的兼容,在使用中會存在一些限制和需要注意的地方倍靡。另一方面,token 更容易在 IOS 和 Android 上實(shí)現(xiàn)课舍,Token 也更容易實(shí)現(xiàn)物聯(lián)網(wǎng)應(yīng)用程序和服務(wù)塌西,沒有 Cookie 存儲的概念。
共同的問題
這里介紹一些常見的問題筝尾,或者在令牌 token 的情況下出現(xiàn)時經(jīng)常出現(xiàn)捡需。這里的關(guān)鍵重點(diǎn)將是安全性,但我們將介紹有關(guān) token 大小筹淫,存儲和加密的用例站辉。
- JWT 大小
token 最大的缺點(diǎn)就是它的大小,最小的它都比 cookie 要大损姜,如果 token 中包含很多聲明局义,那問題就會變得比較嚴(yán)重芥映,畢竟向服務(wù)器發(fā)送的每個請求都要有這個 token。(意思應(yīng)該是太大了會導(dǎo)致請求緩慢) - 哪里存儲 token
通常 JWT 被存儲在瀏覽器的 local storage 中并且能夠很好的運(yùn)用,但是這樣存儲也會有問題泛源,不像 cookie,local storage 被沙盒化到特定域披粟,其區(qū)域不能被任何其他域訪問碧绞,包括子域。
你可以存儲 token 在 cookie 中比规,但是 cookie 最大的大小也只有 4kb若厚,所以如果你有許多聲明的時候可能會存儲不夠,session storage 就更不用說了蜒什,會話斷開就被清除掉了测秸。
(個人記錄:由于JWT前兩個字符串采用base64進(jìn)行編碼,所以內(nèi)容越多,編碼字符串長度越長) - XSS 和 XSRF 防護(hù)
保護(hù)用戶信息安全和服務(wù)器數(shù)據(jù)始終是首要任務(wù)乞封。最常見的網(wǎng)絡(luò)攻擊就是 XSS 和 CSRF做裙。
如果用戶輸入或提交的信息沒有得到過濾的話,如果一些攻擊代碼可以在你訪問的域名下執(zhí)行肃晚,那你的 JWT token 會被泄露锚贱。與 CSRF 相比,XSS 會更容易處理(因?yàn)?XSS 很容易理解)关串。許多框架包括 Angular 等等拧廊,都會自動過濾掉 input 輸入的某些內(nèi)容且防止任意代碼執(zhí)行。如果框架本身不自帶這種過濾機(jī)制晋修,可以采用一些插件比如 caja吧碾。這種過濾 input 輸入框是大部分框架和語言解決 XSS 問題的一個方式。
如果你通過 local storage 使用 JWT墓卦,那么可以避免 CSRF倦春,但是另一方面,如果你用 cookie 來存儲 JWT落剪,那就需要防護(hù) CSRF睁本,CSRF 并不像 XSS 攻擊那么好理解。解釋 CSRF 攻擊可能會非常耗時忠怖。為了避免過度簡化呢堰,防止 CSRF 攻擊,你的服務(wù)器會和客戶端建立會話后凡泣,會生成一個唯一的 token(這不是 JWT)枉疼。然后隨時將數(shù)據(jù)提交到你的服務(wù)器,隱藏的 input 將會包含這個 token鞋拟,服務(wù)器將會檢查這個 token 以確保 token 匹配骂维。不夠我們是建議將 JWT 存儲在 local storage 中,你也不太需要擔(dān)心 CSRF 攻擊的問題贺纲。
其中一個最好的保護(hù)用戶和服務(wù)器的方法就是有一個給 token 一個短期過期時間席舍,這樣即使 token 被其他人獲取,但是也會很快不能再用哮笆。此外来颤,你可以維護(hù)一個受攻擊的 token 黑名單,不允許這些黑名單的 token 訪問系統(tǒng)稠肘。最后福铅,還可以統(tǒng)一更改 token 算法,讓所有 token 都失效项阴,并讓用戶重新登錄系統(tǒng)滑黔,不過這種方法一般不推薦笆包,只有在被攻擊比較嚴(yán)重的情況下使用。 - token Are Signed,Not Encrypted
一個 JWT 是由三部分構(gòu)成:header,payload,signature略荡。
header: 算法和 token 類型庵佣,如
{ "alg": "HS256", "typ": "JWT"}
payload: 數(shù)據(jù),如
{ "sub": "12", "name": "Ado Kukic", "admin": true}
驗(yàn)證簽名
HMACSHA256(
base64UrlEncode(header)+"." +
base64UrlEncode(payload),
secret
)
JWT 的格式為 header.payload.signature
汛兜,如果我們需要通過 HMACSHA256 算法簽發(fā)一個 JWT 巴粪,如果 secret 是 'shhhh' 以及 payload 是
{ "sub": "1234567890", "name": "Ado Kukic", "admin": true}
則 JWT 為
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFkbyBLdWtpYyIsImFkbWluIjp0cnVlLCJpYXQiOjE0NjQyOTc4ODV9.Y47kJvnHzU9qeJIN48_bVna6O0EDFiMiQ9LpNVDFymM
(疑問:secret 在這里是干嘛的)
這里有一個非常重要的提醒,token 是通過 HMACSHA256 算法生成粥谬,header 和 payload 是 Base64URL 編碼肛根,這不是加密。如果你去訪問 jwt.io 網(wǎng)站漏策,粘貼 token 以及選擇 HMACSHA256 算法派哲,可以解析出 token 讀取它詳細(xì)的內(nèi)容。因此里面不能攜帶一些敏感數(shù)據(jù)掺喻,比如密碼芭届,絕對不能存儲在 payload 中。
如果你必須存儲敏感信息在 payload 中感耙,或者使 JWT 被隱藏喉脖,你可以使用 JWE(JSON Web Encryption)。JWE 允許你去加密 JWT 的內(nèi)容抑月,讓其除了服務(wù)器以外的任何人都不可讀。JOSE 為 JWE 提供了一個很好的框架舆蝴,并為許多流行框架(包括 NodeJS 和 Java)提供了 SDK谦絮。
token 驗(yàn)證在 Auth0 上的實(shí)踐
未完待續(xù)...