上一講談到 Session-Based 及 Token-Based 的驗證,兩種方式在後端的實作方式不太一樣,Session-Based 能夠保存狀態,而 Token-Based 則無法。但在前端的實作上,無論是 Session ID 或是 Token,都需要先暫存在 Browser 當中。
而這講我們就來簡單聊聊 Browser 儲存、送出登入資訊的機制,以及儲存的資訊有沒有被洩露的風險?
當我們透過登入的 API 得到 Session ID 或 Token 時,為了避免重新整理或是重開視窗造成資料遺失,不會將其存在記憶體中,通常都會其他方式將其儲存下來。
在現代的瀏覽器中,有提供給 JavaScript 使用的 API,稱作 Web API。而 Local Storage 和 Cookies 便是用來暫存 Client 端資料的常見 JavaScript API。
這兩種 API 便常用來儲存 Session ID 和 Token 這種維持登入中的資訊,因為當 Browser 被關閉、重新開啟之後,這些資訊依然會存在,這就是為何當我們輸入帳號密碼登入某網站之後,下次重新開啟網頁還會是登入的狀態。
兩種方式的 API 使用都相當容易,如果要寫入一組 key-value pair,例如 token,則可以這樣撰寫:
// Local Storage
localStorage.setItem('token', 'some-token');
// Cookies
document.cookie = "token=some-token";
要取得存入的資料時,Local Storage 可以直接呼叫 localStorage.getItem
;而呼叫 cookie 的 document.cookie
則會得到 key1=value1; key2=value2
這樣用 ;
分隔的字串,稍微做點處理也能到我們需要的 Value。
能夠讀取儲存在前端的登入資訊後,下一個步驟就是將此資訊附帶在送往後端的 HTTP Request 之中。
HTTP 的 Method 大致可以分成有 Body 和沒有的兩類,例如 POST 有 Body 而 GET 沒有。為了符合 HTTP 的協定內容,我們要傳輸的資料能考慮放在 URL 之中,例如 http://my.website?token=some-token
;或是在 POST 這類 Method 能放在 Body 當中。
然而,放在 URL 中會有 token 存放在多個地方的資安風險,因為 Browser 會將歷史記錄儲存下來,也包含瀏覽過的 URL。
放在 Body 當中相對來說安全一些,但是在考慮到我們的前端程式可能會送出不同種 Method 的 Request,在 POST 能用而 GET 不行,就會有不一致的問題,可能也不是太好的做法。
因此,最常見的方法是將此登入資訊放在 HTTP Header 當中,這是 HTTP 專門設計來放置一些 Meta Data 的欄位。
我們可以在 Headers 的 Authorization
放入 Token,或是 X-Session-ID
放入 Session:
const headers_for_token = {'Authorization': `Bearer ${token}`};
const headers_for_session_id = {'X-Session-ID': sessionId};
其中 Authorization
用來附帶各種身份驗證的資訊,如能直接附帶帳號密碼 Basic,和 OAuth 2.0 定義的 Bearer。
而 Bearer 有攜帶的意思,意味只要此 Request 攜帶後面的 Token,無論裝置或設備都可以用此驗證。然而就算不用遵從 HTTP 的 Authorization
Header 或是 OAuth 2.0 的 Bearer,只要前後端都是我們設計的,就只要想辦法在前端把 Token 放入 Header,並在後端解析即可。
如同我們可以傳遞的 Session ID:這個 X-Session-ID
Header 並非 HTTP 的標準內容,由 X-
開頭的名稱是我們可以自定義的 Header。
那麼,當我們實作登入系統時,在前端應該選用哪種方式來儲存登入資訊呢?
先來看看兩者的差異,其中 Local Storage 適合儲存比較大的資料,記錄 5MB 的資訊不在話下,但是 Cookies 卻僅僅能儲存 4KB 的資訊。
另外一個主要差異則是在於 Local Storage 儲存的資料沒有所謂的期限,只要前端程式不刪、使用者也不刪,就能一直留在上面;但是 Cookies 則一定要設定一個期限,如果不設定的話便會在視窗被關掉後資料便消失。
雖說 Cookies 存的東西少,又有期限限制,但是回到需求面,如果我們只要單純儲存 Session ID 和 Token 這麼少的資訊,完全是足夠的,並且登入資訊這類如同鑰匙的資訊,留存的時間本來便不該太長,定期更換才是更安全的做法,有了期限設定讓瀏覽器自動刪除也是相當合理的,反觀 Local Storage 需要額外寫程式來刪除過期的登入資訊。
除此之外,其實還有更多資安的原因,例如 Local Storage 在 XSS、CSRF 等攻擊上比較難以抵禦,相反的 Cookies 有其他機制能夠讓資訊的儲存和傳遞更安全。
因此,在兩者實作的難易程度不會差太多之下,我們應該因為資安問題而首選 Cookies 來儲存登入後的 Session ID 及 Token。