昨天講了在產生 session ID 時有什麼應該要注意的地方,但除了 session 之外,現在也很流行用 JSON Web Token(JWT)做認證授權,所以今天要來說一說關於 JWT 的安全知識
用過 JWT 的人雖多,但可能不是每個人都熟悉他的原理,所以這邊簡單介紹一下,通常一個 JWT 會長得像下面這一大串,仔細看他其實可以根據 .
分隔三個部分,前兩部分分別是 Header 跟 Payload,Header 裡面通常會寫他是用什麼演算法進行簽章,讓這個 JWT 不會被偽造,而 Payload 則是放這個 token 想要儲存在瀏覽器上的內容。
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySUQiOjEyMzQ1LCJleHAiOjE2Mzk1NDgxMjF9.TuDvzeiQagBS_aFOBgVHvTGSol4Cu8-NXHdJTaPRMJ4
Header | Payload | Signature
而且他們都是由 JSON 格式的資料經過 Base64 編碼後出來的(為了方便傳輸),如果想看他們原本長什麼樣子只要用 Base64 解碼回去就好了,譬如這串 JWT 的 header 就是 {"alg":"HS256"}
代表他用的簽章演算法是 HMAC 加上 SHA256(看不懂沒關係XD,這很正常),而 payload 則是 {"userID":12345,"exp":1639548121}
因此當使用者帶著這串 token 發請求給 API Server,Server 就會先根據 Signature 檢查這串 token 有沒有被修改過,沒有的話就把 payload 解開來看看,如果裡面有 userID,那就代表這個使用者已經登入,可以進行各種操作
因為 JWT payload 裡面的東西雖然不會被篡改(改了 signature 就不對了),但他還是可以在 client 端被解開看到的,所以只能在裡面放一些被看到也沒事的東西,譬如說 userID、username、email 之類的
如果你為了自己方便在裡面放了一些敏感資料,譬如說使用者的住址、密碼之類的,否則萬一這串長長的 token 不小心洩漏出去了,只要用 base64 把它解開就可以得到使用者的密碼
在上面的範例中,payload 被解開之後有一個 "exp": 1639548121
就是指這個 token 什麼時候會過期,而且因為 payload 不能被篡改,所以一旦 server 看到 token 已經過期了,那就會要求使用者重新登入
那為什麼 token 一定要有期限呢?讓使用者一個 token 用一輩子難道不好嗎?
因為 JWT 不像 session ID 會存在資料庫裡面,一旦發行出去之後就沒有辦法手動撤銷(revoke),只能等他自己過期,所以如果給了使用者一個一輩子都不會過期的 token,而且好巧不巧的這個 token 被駭客偷了去去,那駭客一輩子都可以使用這個 token 來進行操作
所以比較好的方式是每個 token 都給短短的時間(譬如說 30 分鐘),如果使用者送來的 token 剛過期不久那就換一個新的給他,避免他三不五時就要重新登入。但如果真的過期太久了,譬如說已經超過七天,那代表這個禮拜使用者都沒有上線用你的網站,這時候就可以直接拒絕他,請他重新登入之後再發給他全新的 token 給他
在 Facebook 上的技術討論社團待久了,三不五時就會看到有人在問 JWT 到底該放在哪裡,是 localStorage、sessionStorage 還是 cookie?因為 JWT 就代表使用者的身份,而且有了這個 token 之後就可以用它來做任何事,所以應該要放在最安全的地方,也不能讓不需要他的人可以碰到他
有了目標之後,我來們一一檢視這三個選項:首先是 localStorage,放在裡面的東西不只不會不見,而且任何第三方的腳本如 jquery、bootstrap 都可以取得裡面的東西(但他們明明就不需要),所以顯然 localStorage 不是一個很好的選項;而 sessionStorage 的話雖然每次瀏覽器關閉後都會清掉,但其實對於安全性沒有太大幫助,因為第三方腳本還是可以拿到裡面的東西,而且每次重開瀏覽器就要重新登入對使用者來說應該非常困擾
而 cookie 的話剛好非常符合需求,因為我們可以為他加上 HttpOnly 跟 Secure 屬性,如此一來 JWT 就不會被第三方腳本偷走、就算中了 XSS 也沒關係,而且傳輸的過程中也不會被中間人偷走,如果不嫌麻煩的話再加上 SameSite 又可以防 CSRF 攻擊,可以說是非常好的解決方案。
所以如果哪天又有人問你 JWT 要放哪裡才好時,不用猶豫,直接告訴他放在 cookie 加上 HttpOnly 跟 Secure 屬性,才能保護好你的 JWT 喔
JWT 作為近幾年來新興的認證授權解決方案,除了不用像 Session ID 存進資料庫之外,也更適合分散式應用。但記得在使用 JWT 時不要忘記這些注意事項,才可以讓你的網站既安全又方便哦~
關於今天的內容有什麼問題歡迎在下方留言,沒問題的話我們就明天見囉~
提及一下缺點:
因為 JWT 不會與伺服器端分享和同步狀態,這就代表如果需要撤銷已頒發的權杖,或是臨時變更權杖的效期,都是無法實現的,只能等待 JWT 自然到期後不再頒發新權杖或設定新權杖的效期而已。
當然,將欲撤銷的權杖寫入資料庫也是可行(黑名單),但是每次都要讀取資料庫來檢查權杖,這樣不僅沒有充分利用「無狀態」的優勢,甚至與使用 JWT 的初衷背道而馳了!