整理這些技術筆記真的很花時間,如果你願意 關閉 Adblock 支持我,我會把這份感謝轉換成更多「踩坑轉避坑」的內容給你!ヽ(・∀・)ノ
Token 放 localStorage?sessionStorage?還是 Cookie?

前言
這一篇文章來分享與記錄一下關於 JWT Token 的一些事情,以及為什麼不要把 JWT Token 放在 localStorage。
JSON Web Token(JWT)
那麼這邊也簡單描述一下什麼是 JSON Web Token(JWT) 呢?基本上 JWT 是一個基於 JSON 標準的格式,那…它常見在哪些場景呢?
基本上需要授權的地方都可以使用 JWT,例如…
- 登入
- 重設密碼
- 修改個人資料
等等。
因此當使用者登入的時候,你就會得到一組 JWT Token

而這一組 Token 就會是你未來跟伺服器溝通的識別碼,而伺服器也會透過這一組 Token 來驗證你的身份。
JWT Token 的組成
接下來我們來快速聊一下關於 JWT 的組成,底下是 JWT 官方網站所提供的範例 Token
1 | |
JWT Token 基本上是由三個部分組合而成,分別是…
- HEADER(標頭):ALGORITHM & TOKEN TYPE
- 用來說明這一組 Token 是用什麼演算法加密的,以及 Token 的類型
- PAYLOAD(負載):DATA
- 主要是用來存放一些資料,例如使用者的 ID、名字等等
- VERIFY SIGNATURE(驗證簽名)
- 這一部分是用來驗證 Token 是否為正確的 Token
那麼我們該如何看出 HEADER、PAYLOAD 與 VERIFY SIGNATURE 呢?基本上 JWT Token 是使用 . 來分隔的,因此我們可以透過 . 來切割 Token,拆成以下:
1 | |
到目前為此,應該已經對於 JWT Token 的組成有一定的概念了,所以接下來就來快速聊一下瀏覽器的儲存空間吧。
localStorage?sessionStorage?cookie?
當我們登入之後會取得一組 JWT Token 作為與後端伺服器溝通的識別碼,那麼再往下介紹之前,我想先快速重溫一下關於瀏覽器儲存資料的地方。
底下我列出幾個常見的地方:
localStoragesessionStoragecookie
接下來我們先來認識一下 localStorage 與 sessionStorage,這兩個都是 HTML5 所提供的 Web Storage API,而這兩個的差別在於 localStorage 的資料會永久(或半永久,因為如果使用者刪除的話)儲存在瀏覽器中,而 sessionStorage 的資料只會存在於當前的瀏覽器分頁中。
接著我們來看一下 localStorage 的基本用法
1 | |
透過上面程式碼,我們可以將資料儲存到瀏覽器的儲存空間內,因此你可以在開發者工具中找到它

那麼 sessionStorage 的用法也是類似的,只是將 localStorage 改成 sessionStorage 即可
1 | |
而且它也可以在開發者工具中找到,只是位置就不會在 localStorage 的位置,而是瀏覽階段儲存空間中

那麼 sessionStorage 與 localStorage 最大不同的地方在於,當你的瀏覽器分頁關閉之後 sessionStorage 的資料就會被清除,而 localStorage 的資料則會一直存在於瀏覽器中。
接下來我們接著看 Cookie 的寫法,Cookie 也是拿來儲存一些資料的,而儲存方式主要是採用 key=value 的方式,並使用 ; 來分隔不同的資料,例如
1 | |
因此寫入時,就可以這樣寫入
1 | |

那麼上面就是我們最基本的 localStorage、sessionStorage 與 cookie 的用法。
(當然上面還有很多細節沒提到,為了讓我可以後面有東西講,所以我特別簡化了。)
JWT Token 該放…?
那麼所以該把 JWT Token 放在哪裡呢?透過前面的範例講解之後,其實我們可以知道 JWT Token 是一個非常重要的識別碼,我們要跟後端拿任何資料都會需要它,因此這一組 Token 是不可以隨意外流的,如果被不法之徒拿到的話,就有可能造成資料外洩的問題。
但是你知道嗎?看似加密的 JWT Token 其實是可以反向解析的,也就是說如果有人拿到了 JWT Token 就可以透過解析的方式來看到/取得裡面的資料。
舉例來講,以官方所提供的 JWT Token 為例:
1 | |
你是可以在官方網站上面直接解析,而且它還會告訴你這個 JWT Token 的加密方式是什麼,以及裡面的資料是什麼

你可以看到 PAYLOAD 直接顯示著以下資料
sub:代表著 Subject,也就是使用者的 IDname:代表著使用者的名字iat:代表著 Issued At,也就是這個 JWT Token 的發行時間
當然 PAYLOAD 的內容是可以由後端開發者決定的,因此你可以在裡面放任何資料,例如你為了方便前端呈現使用者的 Email 或是使用者的姓名等等。
那麼這邊就簡單示範一下,底下我寫了一個簡單的 Express+JWT 範例給予參考
1 | |
當我們輸入正確的帳號密碼之後,就會拿到一組 JWT Token,而這組 JWT Token 的內容就會是以下這樣
1 | |
接著你再拿這一組 Token 去解析,就可以得到以下結果

而你可能就會將這一組 Token 儲存到 localStorage 裡面,並且在每次發送請求的時候都帶上這一組 Token,讓後端可以透過這一組 Token 來判斷你的身份,這樣就可以達到驗證的效果,到目前為止這邊都看似沒有什麼太大的問題。
以我個人立場來講,我認為…
「將 Token 儲存在 localStorage 中並不是一件好事。」
為什麼呢?假設駭客可以在你的網站在執行 JavaScript,那麼也就代表著駭客可以透過 JavaScript 來取得你的 localStorage 裡面的資料,那麼這樣就會有可能造成資料外洩的問題。
底下這邊我寫了一個範例:
你可以看到上面的範例中,我在 CodePen 額外注入了一個 CodePen iFrame,然後直接透過 JavaScript 輕輕鬆鬆地取得 localStorage 的資料。
雖然 localStorage 會依據網域來做區分,但是如果你的網站有 XSS 的漏洞,那麼駭客就可以透過 XSS 的漏洞來取得你的 localStorage 裡面的資料並傳送給自己,那 sessionStorage 呢?其實它的狀況跟 localStorage 是差不多的,所以就不額外示範了。
那麼 localStorage 跟 sessionStorage 都會有這樣的問題,所以使用 Cookie 就可以解決嗎?
答案是不行,因為 Cookie 也有可能會被 XSS 的攻擊給竊取
講那麼多好像不論放 localStorage、sessionStorage 或是 Cookie 好像都會有被竊取的問題?所以接下來這邊我會講四個結論,來盡可能避免這些狀況的發生。
第一個結論:小心第三方資源的引用
基本上只要你的網站上可以執行第三方的 JavaScript,那麼你的資料就有可能會被竊取,這也是為什麼我們會盡可能避免使用 CDN 的原因之一,因為你無法確定 CDN 的資源是否安全,所以不論第三方的東西是否安全,只是出現第三方的東西,那麼就有可能會發生資料外洩的問題。
更不用說如果 CDN 突然掛掉、更新版本等問題。
第二個結論:遵守原則
正常來講是可以使用 HTTP Only 的 Cookie 來解決,只要有被設置成 HTTP Only 的 Cookie,那麼就不會被 JavaScript 給取得,但是由於現今開發都是採用前後端分離的架構,而 HTTP Only 又必須透過後端來設定,因此就會無法設定 HTTP Only 的 Cookie。
(如果沒有前後端分離的話,那麼也可以透過後端來設定 HTTP Only 的 Cookie 的。)
所以如果想要解決無法設置 HTTP Only 的問題的話,就會建議遵守以下原則
- 不將敏感資訊放進 JWT 中,如:密碼、信用卡號等
- 使用 HTTPS 傳輸協議
- 使用短期 Token
- 設置 CORS 的白名單,只允許特定的網域來存取 API
- 使用 CSRF Token 來防止 CSRF 攻擊
- 使用一些語法時,要注意 XSS 的問題,如:
innerHTML、eval等
基本上遵守以上原則就可以避免大部分的問題。
第三個結論:使用 Cookie
為什麼不建議將 Token 放置在 localStorage 裡面呢?因為 localStorage 是屬於半永久的儲存空間所以並不會隨著時間到期而消失,因此如果你的 Token 不會過期的話,那麼隨著時間拉長,那麼就會有隱藏的風險存在,更不用說你可能在裡面存放一些敏感資料的問題。
雖然 localStorage 在設計上是非常方便的,但是跟 Cookie 相比,反而會建議使用 Cookie,因為 Cookie 有額外提供一些選項可以讓你設定 Cookie 的有效期限,因此你可以透過這個有效期限來讓 Cookie 在一定時間之後就會消失,這樣也不用擔心 Token 會永遠存在的問題。
當然你也可以刻意與後端設置不同過期時間,例如:後端 Token 設置 7 天的有效期限,那麼你就可以設置 Cookie 的有效期限比後端更短,例如設置 3 天,這也是一種解決方案,而且你也可以設置該 Cookie 只能在特定的網域下才能存取,當然也可以增加 Refresh Token 機制。
只是這邊要注意 localStorage 的儲存空間大小為 5MB,而 Cookie 的儲存空間大小為 4KB,因此如果你的 Token 過大的話,那麼就會無法使用 Cookie 來儲存,屆時就只能使用 localStorage 來儲存,只是就必須多加注意 localStorage 的安全性問題
第四個結論:使用者的操作
基本上如果都已經盡可能遵守以上,並且系統也有做好安全性更新檢查的話,剩下的部分就會跟使用者比較有關,如果是因為使用者的操作不慎而導致資料外洩的話,那麼就只能說是使用者的問題了,這就不會是開發者的問題。
例如:使用者在公共場所登入,但是沒有登出或是使用者在公共場所登入,但是沒有關閉瀏覽器等等,這些都是屬於使用者本身的問題,這種就比較難以解決,雖然我們可以設計自動登出的功能,但是百密一疏,還是有可能會因為使用者操作不當而外洩 Token。
結尾
雖然使用 localStorage 很方便,但是考慮到一些可能的安全性問題,因此建議還是使用 Cookie 來儲存 Token,並且設置有效期限,這樣就可以盡可能的去避免一些可能的資安問題,所以我將瀏覽器所提供的那幾個儲存空間定義如下
localStorage:半永久的儲存空間,適合儲存一些不會過期的資料,空間雖然有 5MB,但是要注意安全性的問題以及小心 XSS 的問題sessionStorage:短暫的儲存空間,適合儲存一些不重要,頁面關閉就會消失的資料,同上- Cookie:短暫的儲存空間,適合儲存一些會過期的資料,例如:Token 等敏感資訊,但要注意空間僅有 4KB 的問題,以及小心 CSRF 的問題
所以其實不論是那一種儲存空間,都有其適合的使用場景,只是要注意安全性的問題,並且遵守一些原則,這樣就可以盡可能的避免一些可能的資安問題哩
參考文獻
整理這些技術筆記真的很花時間,如果你願意 關閉 Adblock 支持我,我會把這份感謝轉換成更多「踩坑轉避坑」的內容給你!ヽ(・∀・)ノ