淺顯易懂講解如何用JWT來加固API
譯文【51CTO.com快譯】您一定聽說過JSON Web Token(JWT)吧? 它是當前用來保護API的先進技術之一。與大多數(shù)安全概念與技術一樣,我們在準備使用它之前,了解其工作原理是非常必要且重要的。當然,過于專業(yè)和技術性的JWT解釋可能會讓您覺得費解,甚至感到頭痛。那么讓我試著用一種比較淺顯易懂的方式,向您闡述JWT是如何加固API的吧。
API身份驗證
不言而喻,在復雜的網(wǎng)絡環(huán)境中,我們需要對各種API資源實施訪問限制。例如,我們不希望某個用戶能夠更改另一個用戶的密碼。那么,我們就需要該用戶以提交其ID和密碼的方式,來保護和加固目標資源。換句話說:我們需要對他們進行身份驗證。
而在實際應用中,我們保護HTTP類API的難點在于:各種請求是無狀態(tài)的。也就是說:API無法知道任意兩個請求是否來自同一個用戶。有人可能會追問:我們?yōu)槭裁床荒芤笥脩粼诿看握{(diào)用API時,都提供他們的ID和密碼呢?答案是:因為這樣會給用戶帶來極差的訪問體驗。
JSON Web Token
因此,我們需要的是:用戶只用一次性提供信任憑據(jù),而在后續(xù)的請求中,服務器會以另一種方式進行用戶身份的識別?;谶@種思想,JSON Web Token應運而生。
當然,如果您是一位愛好鉆研的學霸,那么您可以通過鏈接:https://robmclarty.com/blog/what-is-a-json-web-token,來對JSON Web Token的工作原理進行全面深入的參悟。
在此讓我們想象一下:如果您打算入住一家酒店,那么“令牌”就是允許您進入自己房間、以及酒店內(nèi)其他設施的安全門卡,顯然您不能進入其他人的房間。而且在退房的時候,您需要退還門卡,即:注銷。
令牌的結(jié)構(gòu)
通常情況下, JSON Web Token是通過各種HTTP請求的頭部(header)被發(fā)送的。如下所示:
- Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U
可見,令牌就是“Authorization:Bearer”的后面部分,隸屬于HTTP的頭部信息。
上述信息雖然顯得比較凌亂,不過它包含了如下部分:
首先,令牌由三個不同的字符串所組成,它們分別以點號隔開。這三個字符串使用了base 64編碼,分別對應頭部(header)、有效載荷(payload)和簽名(signature),如下所示:
- // Header
- eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
- // Payload
- eyJzdWIiOiIxMjM0NTY3ODkwIn0
- // Signature
- dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U
注:base 64是一種轉(zhuǎn)換字符串的方法,能夠確保字符串在跨Web傳輸?shù)倪^程中不會出現(xiàn)問題。由于它不是一種加密方法,因此任何人都可以很容易地對它進行解碼,以查看原始數(shù)據(jù)。
下面,我們對上述字符串進行解碼,以便更好地了解JWT的結(jié)構(gòu)。
頭部
通過解碼令牌的頭部,我們可以得到如下的元信息(meta information)。由于它對于我們理解整體的工作原理幫助不大,所以我們在此并不做詳細解讀。
- {
- "alg":"HS256",
- "typ":"JWT"
- }
有效載荷
有效載荷里的內(nèi)容要豐富得多。您可以用它來包含任何自己需要傳遞的數(shù)據(jù)。在此,由于該令牌的目的是對API的訪問進行身份驗證,因此僅包含了用戶的ID。
- {
- "userId":"1234567890"
- }
值得注意的是:有效負載并不安全。任何人都可以通過解碼令牌,來查看有效負載中的確切內(nèi)容。因此,我們通常只包含ID,而不會包含諸如用戶郵件內(nèi)容等敏感的標識信息。
盡管該有效負載為API提供了識別用戶所需的全部信息,但是它并不提供具體的身份驗證方法。畢竟憑借這些信息,黑客足以能夠輕松地找到用戶的ID,并可偽造出令牌。因此,我們還需要有簽名,而它才是令牌認證環(huán)節(jié)中的關鍵部分。
哈希算法
在開始解釋簽名的工作原理之前,我們需要先來了解一下什么是哈希算法。
首先,它是一個函數(shù),可用來將目標字符串轉(zhuǎn)換為另一種被稱為哈希值(hash)的新字符串。例如,我們對字符串“Hello, world.”進行哈希操作,那么就能夠得到如下經(jīng)過了SHA256哈希算法的輸出:
- 4ae7c3b6ac0beff671efa8cf57386151c06e58ca53a78d83f36107316cec125f
注:哈希算法有許多種不同的類型,JWT常用的是SHA256。
而哈希的重要屬性在于:我們無法使用哈希算法,通過哈希值來識別出原始的字符串。換句話說,我們無法憑借上述哈希值,直接計算或得出原始的字符串“Hello, world.”。從理論上說,根據(jù)哈希的復雜性,猜測出原始字符串是完全不可行的。
JWT簽名
現(xiàn)在,讓我們來看JWT令牌結(jié)構(gòu)的第三個部分:簽名。實際上,該部分是需要進行計算的。
- HMACSHA256(
- base64UrlEncode(header) + "." + base64UrlEncode(payload),
- "secret string"
- );
我們下面來具體分析一下上述代碼:
- 首先,HMACSHA256是哈希函數(shù)的名稱,它用到了兩個參數(shù):需要進行哈希的字符串和密鑰(secret)。
- 其次,這個需要進行哈希的字符串,是經(jīng)過base 64編碼過的頭部和有效載荷。
- 第三,密鑰是一串任意數(shù)據(jù),而且只有服務器知曉。
問:為什么要將頭部和有效載荷添加到簽名的哈希值中呢?
答:這樣可以確保簽名對于該特定令牌來說是僅有的。
問:什么是密鑰?
答:讓我們從如何偽造一個令牌的角度來回答該問題。我們之前說過,黑客無法從輸出值來推導出經(jīng)過哈希的輸入信息。但是,由于簽名中包括了頭部和有效載荷,而這些都是公共的信息,因此如果黑客知道了哈希算法(這通常是在頭部被指定的),那么就能夠生成相同的哈希值。
可見,如果服務器掌握了某個非公開的密鑰,并且將其包含在哈希處理的過程中,那么就能夠防止黑客自行偽造并生成帶有哈希值的令牌。同時,由于哈希值“掩蓋”了各種原始信息,因此也就保證了密鑰不會被黑客所發(fā)現(xiàn)。
注:將私有數(shù)據(jù)添加到哈希之中的過程,被稱為加鹽(salting),這使得破解令牌幾乎是不可能的。
身份驗證過程
至此,想必您已經(jīng)理解了令牌的創(chuàng)建過程。那么,我們又該如何用它來驗證用戶的API呢?
登錄
在用戶登錄時,系統(tǒng)會生成一個令牌,并將其與用戶模型(model)一起存儲在數(shù)據(jù)庫中。
- logincontrol.js:
- if (passwordCorrect) {
- user.token = generateToken(user.id);
- user.save();
- }
然后作為對于登錄請求的響應,該令牌被添加到authorization的頭部。
logincontrol.js:
- if (passwordCorrect) {
- user.token = generateToken(user.id);
- user.save();
- res.headers("authorization",`Bearer ${token}`).send();
- }
驗證請求
有了令牌,用戶現(xiàn)在就可以將其添加到各種后續(xù)的請求中,以驗明正身了。
而當服務器收到添加了身份信息的令牌請求后,會進行如下操作:
- 對令牌進行解碼,并從有效載荷中提取ID。
- 使用此ID,在數(shù)據(jù)庫中查找該用戶的信息。
- 將請求令牌與帶有用戶模型的存儲令牌進行比較。如果匹配,則認定該用戶的“合法”身份。
authMiddleware.js:
- const token = req.header.token;
- const payload = decodeToken(token);
- const user = User.findById(payload.id);
- if (user.token = token) {
- // Authorized
- } else {
- // Unauthorized
- }
注銷
如果該用戶要求注銷,那么系統(tǒng)只需刪除掉當前已添加到用戶模型的令牌便可。由于用戶手上的令牌及時失效了,因此如果他需要再次登錄的話,應重新產(chǎn)生新的令牌。
logoutcontrol.js:
- user.token = null;
- user.save();
總結(jié)
通過上面的逐步分析,希望您能夠?qū)τ谌绾问褂肑SON Web Token來加固API,已經(jīng)建立起了基本的概念。當然,該話題涵括的內(nèi)容遠不止這些,如果您有興趣的話,可以通過如下鏈接進行擴展閱讀:
- Jwt.io - https://jwt.io/
- 什么是JSON Web Token?- https://robmclarty.com/blog/what-is-a-json-web-token
【51CTO譯稿,合作站點轉(zhuǎn)載請注明原文譯者和出處為51CTO.com】