在哪裏存儲你的JWT —— Cookies vs HTML5 Web存儲

作者:Tom Abbott | 2016年1月8日
源文章地址:Where to Store your JWTs – Cookies vs HTML5 Web Storage

前言

要創建一個java應用嗎?JJWT是一個提供端到端JWT創建和驗證的Java庫,由我們自己的Les Hazlewood開發。JJWT永遠是免費和開源的(Apache License, Version 2.0),使用和理解都很簡單。它被設計成一個以構建者爲中心的連貫接口,隱藏了它的大部分複雜性。我們希望你能嘗試一下,讓我們知道你的想法!(如果您是Node開發人員,請查看NJWT)

Stormpath最近正在研究使用JSON Web令牌(JWT)進行令牌身份驗證的特性,我們已經就這些令牌的安全性以及在何處存儲它們進行了多次討論。

如果你想對JWT進行深入瞭解,這篇文章就是爲你準備的。我們將介紹一些基礎知識關於JSON Web令牌(JWT)與OAuth的對比、token存儲在cookies中與存在HTML5 Web存儲(localStorage或sessionStorage)的對比,以及關於跨站點腳本編制(XSS)和跨站點請求僞造(CSRF)。

讓我們開始吧…

JSON Web TOKEN(JWT):速成課程

API認證和授權最常用的解決方案是OAuth 2.0和JWT規範,它們應用十分廣泛。克利夫的筆記時間!以下是你需要知道的關於JWT和OAuth的對比:

  • JWT是一種很好的身份驗證機制。它們提供了一種結構化和無狀態的方法來聲明用戶及其可以訪問的內容。可以對它們進行簽名和編碼,以防止在客戶端進行篡改。
  • JWT是聲明關於token和身份驗證信息的好方法。由於使用JSON,你有大量的自由來決定怎麼使用它,從而讓你的應用更有意義。
  • scopes背後的概念非常強大,但也非常簡單:你可以自由設計自己的訪問控制設計,因爲你使用的是JSON。

如果你遇到一個token,它看起來是這樣的:

"dBjftJeZ4CVP.mB92K27uhbUJU1p1r.wW1gFWFOEjXk…"

這是一個Base64編碼的字符串。如果你把它拆開,你會發現三個獨立的部分:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
.
eyJpc3MiOiJodHRwOi8vZ2FsYXhpZXMuY29tIiwiZXhwIjoxMzAwODE5MzgwLCJzY29wZXMiOlsiZXhwbG9yZXIiLCJzb2xhci1oYXJ2ZXN0ZXIiXSwic3ViIjoic3RhbmxleUBhbmRyb21lZGEuY29tIn0
.
edK9cpfKKlGtT6BqaVy4bHnk5QUsbnbYCWjBEE7wcuY

第一部分是描述token的標頭。第二部分是一個有效負載,它包含豐富的信息,第三部分是一個簽名散列,可以用來驗證token的完整性(如果你有用於簽名的密鑰)。

當我們解碼第二部分,有效載荷,我們得到這個完整的JSON對象:

{
  "iss": "http://galaxies.com",
  "exp": 1300819380,
  "scopes": ["explorer", "solar-harvester", "seller"],
  "sub": "[email protected]"
}

這是令牌的有效負載。它讓你知道以下內容:

  • 這個人是誰(sub, subject的縮寫)
  • 這個人可以使用這個令牌訪問什麼(範圍)
  • 令牌到期時(exp)
  • 誰發行了令牌(iss,發行人的簡稱)

這些信息稱爲“聲明”,因爲令牌創建者聲明瞭一組可用於“瞭解”有關主題的內容的斷言。因爲令牌是用密鑰簽名的,所以您可以驗證它的簽名並隱式地信任所聲明的內容。

令牌是在用戶提供一些憑證(通常是用戶名和密碼)之後提供給用戶的,但是它們也可以提供API密鑰,甚至來自其他服務的令牌。這一點很重要,因爲向API傳遞令牌(可能過期,範圍有限)比傳遞用戶名和密碼更好。如果用戶名和密碼在中間人攻擊中泄露,這就像給攻擊者城堡的鑰匙。

Stormpath的API密鑰認證功能就是一個例子。其思想是,你只提供一次硬憑證,然後獲得一個令牌來代替硬憑證。

JSON Web Token (JWT)規範正在迅速獲得關注。Stormpath強烈推薦它,它提供了結構和安全性,但是可以根據應用程序靈活地修改它。這裏有一篇更長的文章:正確使用JWT

在哪裏存放你的JWT

所以現在你已經很好地理解了JWT是什麼,下一步是找出如何存儲這些token。如果你正在構建一個web應用程序,你有兩個選擇:

  • HTML5 Web存儲(localStorage或sessionStorage)
  • cookies

爲了比較這兩者,假設我們有一個構建好的的AngularJS或單頁面應用程序(SPA) galaxies.com,它有一個登錄路由(/token)來驗證用戶,並返回JWT。要訪問服務於SPA的其他API端口,客戶端需要傳遞一個有效的JWT。

單頁應用程序發出的請求類似於:

HTTP/1.1
 
POST /token
Host: galaxies.com
Content-Type: application/x-www-form-urlencoded
 
[email protected]&password=andromedaisheadingstraightforusomg&grant_type=password

服務器的響應將根據您使用的是cookie還是Web存儲而有所不同。爲了進行比較,讓我們來看看你是如何做到這兩點的。

本地存儲或sessionStorage (Web存儲)

爲JWT交換用戶名和密碼以將其存儲在瀏覽器存儲(sessionStorage或localStorage)中是相當簡單的。響應體將包含JWT作爲訪問令牌:

HTTP/1.1 200 OK
 
{
	"access_token": "eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB",
    "expires_in":3600
}

在客戶端,你會將令牌存儲在HTML5的Web存儲中(假設我們有一個成功的回調):

function tokenSuccess(err, response) {
    if(err){
        throw err;
    }
    $window.sessionStorage.accessToken = response.body.access_token;
}

要將訪問令牌傳遞迴受保護的api,需要使用HTTP授權頭和Bearer scheme。你的SPA會提出類似的要求:

HTTP/1.1
 
GET /stars/pollux
Host: galaxies.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB

Cookie存儲

爲JWT交換用戶名和密碼以將其存儲在cookie中也很簡單。響應將使用Set-Cookie HTTP頭:

HTTP/1.1 200 OK
 
Set-Cookie: access_token=eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB; Secure; HttpOnly;

要將訪問令牌傳遞迴同一域中的API,瀏覽器將自動包含cookie值。向API發出的請求類似於:

GET /stars/pollux
Host: galaxies.com
 
Cookie: access_token=eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB;

兩者區別

如果您對這兩種方法進行比較,兩者都將接收到一個JWT到瀏覽器。兩者都是無狀態的,因爲API需要的所有信息都在JWT中。兩者都很容易傳遞迴受保護的api。區別在於存儲媒介。

sessionStorage and localStorage 安全性

可以通過同一域中的JavaScript訪問Web存儲(localStorage / sessionStorage)。這意味着在你的站點上運行的任何JavaScript都可以訪問web存儲,因此很容易受到跨站點腳本攻擊 (XSS) 。簡單地說,XSS是一種漏洞,攻擊者可以在其中注入將在你的頁面上運行的JavaScript。XSS攻擊者試圖通過表單輸入注入JavaScript,攻擊者將<script>alert('You are Hacked');</script>提交到表單中,看瀏覽器是否會執行這段腳本並被其它用戶看到。

爲了防止XSS,通常的應對是轉義和編碼所有不可信的數據。但這遠遠不是故事的全部。2015年,現代web應用使用基於CDNs或外部的JavaScript。現代的web應用程序包括用於A/B測試、funnel/market分析和廣告。我們使用像Bower這樣的包管理器將其他人的代碼導入到我們的應用程序中。

如果您使用的腳本中只要有一個被破壞了怎麼辦?惡意的JavaScript可以嵌入到頁面中,Web存儲也會受到影響。這些類型的XSS攻擊可以在不知情的情況下獲取訪問您站點的每個人的Web存儲。這可能就是爲什麼許多組織建議不要在web存儲中存儲任何有價值的東西或任何認證信息的原因,這包括會話標識符和令牌。

作爲一種存儲機制,Web存儲在傳輸期間不強制執行任何安全標準。無論誰讀取Web存儲並使用它,都必須確保始終通過HTTPS而不是HTTP發送JWT。

cookie 安全性

當與有HttpOnly標誌的cookie一起使用時,不能通過JavaScript訪問cookie,並且不受XSS的影響。您還可以設置安全cookie標誌,以確保cookie僅通過HTTPS發送。這是在過去使用cookie來存儲令牌或會話數據的主要原因之一。現代開發人員不願使用cookie,因爲他們通常需要將session存儲在服務器上,從而破壞了RESTful 的最佳實踐。如果您在cookie中存儲JWT,那麼cookie作爲一種存儲機制不需要將session存儲在服務器上。這是因爲JWT封裝了服務器服務請求所需的一切。

然而,cookie容易受到另一種類型的攻擊:跨站點請求僞造(CSRF)。CSRF攻擊是一種攻擊類型,當惡意web站點、電子郵件或博客導致用戶的web瀏覽器在當前已通過身份驗證的受信任站點上執行不需要的操作時,就會發生這種攻擊。這利用的是瀏覽器處理cookie的方式,cookie只能被髮送到允許它的域。默認情況下,最初設置cookie的域galaxies.com/,不管你是在galaxies.com還是hahagonnahackyou.com訪問最初的域,cookie都會被附帶在請求中。

CSRF的工作方式是試圖引誘你訪問hahagonnahackyou.com。該站點將有一個img標記或JavaScript來模擬發佈到galaxies.com的表單,並嘗試劫持您的會話(如果session仍然有效)和修改您的帳戶。

例如:

<body>
 
  <!– CSRF with an img tag >
 
  <img href="http://galaxies.com/stars/[email protected]" />
 
  <!– or with a hidden form post >
 
  <script type="text/javascript">
  $(document).ready(function() {
    window.document.forms[0].submit();
  });
  </script>
 
  <div style="display:none;">
    <form action="http://galaxies.com/stars/pollux" method="POST">
      <input name="transferTo" value="[email protected]" />
    <form>
  </div>
</body>

兩者都將發送galaxies.com的cookie,並可能導致未經授權的狀態更改。可以通過使用同步令牌模式來防止CSRF。這聽起來很複雜,但所有現代web框架都支持這一點。

例如,AngularJS有一個解決方案來驗證只有您的域才能訪問cookie。直接看AngularJS文檔:

When performing XHR requests, the $http service reads a token from a cookie (by default, XSRF-TOKEN) and sets it as an HTTP header (X-XSRF-TOKEN). Since only JavaScript that runs on your domain can read the cookie, your server can be assured that the XHR came from JavaScript running on your domain.

翻譯

執行XHR請求時,$http服務從cookie(默認情況下爲XSRF-TOKEN)中讀取一個令牌,並將其設置爲http標頭(X-XSRF-TOKEN)。因爲只有在您的域上運行的JavaScript才能讀取cookie,所以您的服務器可以確信XHR來自於在您的域上運行的JavaScript。

你可以通過包含一個xsrfToken JWT聲明來使這個CSRF保護無狀態:

{
  "iss": "http://galaxies.com",
  "exp": 1300819380,
  "scopes": ["explorer", "solar-harvester", "seller"],
  "sub": "[email protected]",
  "xsrfToken": "d9b9714c-7ac0-42e0-8696-2dae95dbc33e"
}

如果您正在爲AngularJS使用Stormpath SDK,那麼無需開發就可以獲得無狀態的CSRF保護。

利用您的web應用程序框架的CSRF保護使cookie對於存儲JWT非常可靠。還可以通過檢查來自API的HTTP RefererOrigin 頭來部分阻止CSRF。CSRF攻擊會有與應用無關的RefererOrigin頭。

儘管存儲JWT更安全,但cookie可能會給開發人員帶來一些麻煩,這取決於應用是否需要跨域訪問才能工作。請注意,cookie具有可以修改的附加屬性(Domain/Path),以允許您指定允許將cookie發送到何處。使用AJAX,您的服務器端還可以通知瀏覽器是否應該使用CORS請求發送憑據(包括cookie)。

結論

JWT是一種非常棒的身份驗證機制。它們提供了一種結構化的方法來聲明用戶及其可以訪問的內容。可以對它們進行編碼和簽名以防止在客戶端進行篡改,但問題在於細節和存儲它們的位置。Stormpath建議您將JWT存儲在web應用程序的cookie中,因爲它們提供了額外的安全性,並且使用現代web框架可以簡化對CSRF的保護。HTML5 Web存儲容易受到XSS的攻擊,具有更大的攻擊表面積,可以影響所有應用程序用戶,造成成功攻擊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章