譯者 | 張?jiān)霰?/p>
審校 | 梁策 孫淑娟
對(duì)于某些用例,API密鑰就足夠了。但另一些情況下,出于安全和靈活的考慮,還需要JSON Web令牌(JWT)授權(quán)。所以要比較API密鑰和JWT授權(quán)兩個(gè)方案,具體方案還需具體分析。
所有API調(diào)用都需要一定程度的安全性和訪問控制
具有合理ACL的API密鑰可以在不增加太多開銷的情況下提供足夠的安全性。但是,隨著微服務(wù)的使用在各種大小任務(wù)中增加,API生態(tài)系統(tǒng)可能會(huì)需要像JWT授權(quán)方案這樣更統(tǒng)一、更細(xì)粒度、更安全的方案。
適合API密鑰的情況
對(duì)使用基于云的搜索API的在線企業(yè)來說,只要數(shù)據(jù)的基礎(chǔ)索引不包含任何機(jī)密信息,通常可以公開只讀API密鑰,而不會(huì)帶來很大風(fēng)險(xiǎn)。
事實(shí)上,出于性能考慮,客戶端應(yīng)用程序應(yīng)該直接連接到云搜索引擎,從而公開其API密鑰——以避免在進(jìn)入云之前更長(zhǎng)時(shí)間地訪問后端服務(wù)器。 另一方面,索引更新需要限制對(duì)API密鑰的訪問,而這些API密鑰不應(yīng)被公開。
在這兩個(gè)用例(搜索和索引)中,API密鑰通常都很不錯(cuò),無需在JWT 授權(quán)上耗費(fèi)金錢。
何時(shí)該考慮JWT授權(quán)
API需要越來越靈活,也越來越需要保護(hù)。
JWT授權(quán)不僅額外提高了安全級(jí)別(詳見下文),還提供了一種更易于管理、更簡(jiǎn)單的方法來協(xié)調(diào)日常中使用大量的API網(wǎng)絡(luò)。
JWT通過生成包含用戶和應(yīng)用程序級(jí)別信息(加密或散列)的單個(gè)共享令牌來集中身份驗(yàn)證和授權(quán),以幫助同一生態(tài)系統(tǒng)中的所有API確定允許令牌 持有者執(zhí)行的操作。
乍一看,API的密鑰似乎很簡(jiǎn)單,你只需要發(fā)送正確的API密鑰,就可以開始使用,但這好得有點(diǎn)不真實(shí)。
當(dāng)生態(tài)系統(tǒng)依賴于許多集成的微服務(wù)時(shí),大量API密鑰管理將會(huì)變得混亂、不可靠甚至幾乎無法進(jìn)行。
它們會(huì)有數(shù)量增加、更改、過期、被刪除、ACL更改等等情況,而不通知依賴這些相同API密鑰的應(yīng)用程序和用戶。
使用JWT可為單點(diǎn)登錄架構(gòu)奠定基礎(chǔ),下文將詳述這一點(diǎn)。
使用API密鑰與JWT授權(quán)
使用API密鑰
API密鑰直接、簡(jiǎn)單且完全透明。它們不代表任何基礎(chǔ)信息,不加密秘密消息,只是不可讀的唯一ID。
下面是一個(gè)在客戶端javascript中公開可用的API密鑰的示例:
該代碼包括一個(gè)App ID(app-id-BBRSSHR),它使用API密鑰(temp-search-key-ere452sdaz56qsjh565d)進(jìn)行搜索。
App ID 是指一個(gè)面向用戶的應(yīng)用程序(如在線網(wǎng)站或流媒體服務(wù))。API 密鑰是臨時(shí)的,它在一段時(shí)間后就過期,從而具有一定保護(hù)作用,能防止不必要的使用或?yàn)E用。
import { hitTemplate } from "./helpers";
const search = instantsearch({
appId: "app-id-BBRSSHR",
apiKey: "temp-search-key-ere452sdaz56qsjh565d",
indexName: "demo_ecommerce"
});
另一個(gè)例子是索引,它需要一個(gè)更安全的API密鑰。它有相同的格式(appId+apiKey),但其秘鑰要么在編譯代碼中,要么在后端的安全數(shù)據(jù)庫中,因而對(duì)公眾隱藏且為私有。
App ID (YourApplicationID) 是指后臺(tái)系統(tǒng)。 API 密鑰 (YourAdminAPIKey) 可以是一個(gè)永久的管理密鑰,每年只更改一次以簡(jiǎn)化維護(hù)。
use Algolia\AlgoliaSearch\SearchClient;
$client = SearchClient::create(
'YourApplicationID',
'YourAdminAPIKey'
);
$index = $client->initIndex('demo_ecommerce');
$index->saveObject(
[
'firstname' => 'Jimmie',
'lastname' => 'Barninger',
'city' => 'New York',
'objectID' => 'myID'
]
);
使用 JWT 令牌
JWT令牌是一大串不可讀的字符,其中包含的信息已隱藏和編碼,被簽名或加密算法覆蓋。它由三部分組成:頭部(header)、主體(body)和簽名(signature),
由句點(diǎn)分隔:Header.Body.Signature。
EZPZAAdsqfqfzeezarEUARLEA.sqfdqsTIYfddhtreujhgGSFJ.fkdlsqEgfdsgkerGAFSLEvdslmgIegeVDEzefsqd
EZPZAADSQFZEEZAREUARLEA是JWT頭,其中包含以下信息:
{
"alg": "HS256",
"typ": "JWT"
}
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
我們有不同的算法可用,例如 RS256 和 HS256。 這里我們使用 HS256算法,它需要在生成簽名時(shí)使用私鑰,RS256則使用私鑰和公鑰組合。
sqfdqsTIYfddhtreujhgGSFJ是 JWT 主體(稱為有效負(fù)載),其中包含用戶的身份,以幫助確立令牌用戶的權(quán)利。 它還提供過期日期(越短越安全)等其他信息。
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
fkdlsqEgfdsgkerGAFSLEvdslmgIegeVDEzefsqd是JWT簽名 ,它如頭部所示,通過使用 HS256 散列方法組合頭部、主體和共享私鑰組合生成。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
這樣你就得到了相應(yīng)的令牌:Header.Body.Signature:
EZPZAAdsqfqfzeezarEUARLEA.sqfdqsTIYfddhtreujhgGSFJ.fkdlsqEgfdsgkerGAFSLEvdslmgIegeVDEzefsqd
關(guān)于身份驗(yàn)證(Authentication)和授權(quán)(Authorization)
API密鑰和JWT都用于身份驗(yàn)證和授權(quán),但方式有所不同。
- 身份驗(yàn)證(Authentication)允許用戶或應(yīng)用程序使用API的一種或多種方法。
- 授權(quán)(Authorization)則規(guī)定了用戶或應(yīng)用程序如何使用這些方法。一些應(yīng)用程序或用戶只能讀取數(shù)據(jù),一些可以更新,一些則是管理員(角色和權(quán)限)。API密鑰也是如此,由它們的ACL管理,它們可以是只讀、寫訪問或管理。
進(jìn)行身份驗(yàn)證和授權(quán)時(shí),API密鑰使用相同的API密鑰,JWT授權(quán)則需要在生成授權(quán)令牌之前進(jìn)行初始身份驗(yàn)證。
一旦生成了令牌,它就可以在整個(gè)生態(tài)系統(tǒng)中使用,以確定令牌持有者能做什么和不能做什么。
此外,API密鑰驗(yàn)證的是應(yīng)用程序而不是用戶;而JWT同時(shí)對(duì)用戶和應(yīng)用程序進(jìn)行身份驗(yàn)證。當(dāng)然,你可以使用API密鑰進(jìn)行用戶級(jí)授權(quán),但它的設(shè)計(jì)并不完善——生態(tài)系統(tǒng)需要為每個(gè)用戶或會(huì)話id生成和管理API密鑰,這對(duì)系統(tǒng)來說是不必要的負(fù)擔(dān)。
保護(hù)和安全提升
在安全方面,API密鑰和JWT都容易受到攻擊。最好的安全措施是為所有端到端通信實(shí)施安全架構(gòu)。
不過,因?yàn)樗鼈冃枰浑[藏,API密鑰在歷史上不太安全。
你可以使用SSL/TLS/HTTPS隱藏密鑰,或者將密鑰的使用限制在后端進(jìn)程。但是,你不能控制所有API的使用,且API密鑰也可能會(huì)泄漏,同時(shí)HTTPS也并不總是可行方案等等。
在JWT中,由于令牌是散列/加密的,因此它具有更安全的方法,不太可能被公開。
JWT令牌中包含哪些信息?
API密鑰和JWT令牌之間最顯著的區(qū)別在于,JWT令牌是自包含的:它們包含API保護(hù)交易和確定令牌持有人權(quán)利粒度所需的信息。
相反,API密鑰使用其唯一性來獲得初始訪問;但是,API需要在中央表中找到與密鑰關(guān)聯(lián)的ACL,以精準(zhǔn)確定密鑰允許訪問的內(nèi)容。
通常,API密鑰只保障應(yīng)用程序?qū)蛹?jí)的安全,為每個(gè)用戶提供相同的訪問權(quán)限;而JWT令牌提供用戶級(jí)訪問。
JWT令牌可以包含過期日期和用戶標(biāo)識(shí)符等信息,以確定用戶在整個(gè)生態(tài)系統(tǒng)中的權(quán)限。
讓我們看一下JWT令牌中可以包含的一些信息:
- iss(頒發(fā)者):標(biāo)識(shí)發(fā)布 JWT 的主體。
- sub(主題):標(biāo)識(shí)作為 JWT 主題的主體, 必須是唯一的。
- aud(受眾):標(biāo)識(shí) JWT 的目標(biāo)接收者(字符串?dāng)?shù)組/uri)。
- exp(過期時(shí)間):標(biāo)識(shí)過期時(shí)間(UTC Unix),在此之后你必須不再接受此令牌。 它應(yīng)該在發(fā)布時(shí)間之后。
- nbf(not before):標(biāo)識(shí)不能接受 JWT 的 UTC Unix 時(shí)間
- iat(發(fā)布時(shí)間):標(biāo)識(shí)發(fā)布 JWT 的 UTC Unix 時(shí)間。
- jti(JWT ID):為 JWT 提供唯一標(biāo)識(shí)符。
例子:
{
"iss": "stackoverflow",
"sub": "joe",
"aud": ["all"],
"iat": 1300819370,
"exp": 1300819380,
"jti": "3F2504E0-4F89-11D3-9A0C-0305E82C3301",
"context":
{
"user":
{
"key": "joe",
"displayName": "Joe Smith"
},
"roles":["admin","finaluser"]
}
}
JWT授權(quán)具有靈活性、可靠性和更高的安全性
下面是一個(gè)使用場(chǎng)景:
- 你有多種應(yīng)用程序,這些程序可能:
- 允許我們跟蹤所有用戶API使用情況;
- 允許訪問賬單和客戶數(shù)據(jù);
- 允許API用戶更改在不同系統(tǒng)上設(shè)置;
- 檢索產(chǎn)品數(shù)據(jù)或業(yè)務(wù)內(nèi)容等。
使用API密鑰操作
當(dāng)有很多的API在運(yùn)行時(shí),就會(huì)出現(xiàn)問題。這些訪問所需的 100 多個(gè) API 密鑰要存儲(chǔ)在哪里呢?管理過多的API密鑰需要為生態(tài)系統(tǒng)中運(yùn)行的所有應(yīng)用程序提供一個(gè)API密鑰表。
因此,生態(tài)系統(tǒng)中的每個(gè)應(yīng)用程序都必須了解數(shù)據(jù)庫,都需要連接并讀取該密鑰表。此外,一些應(yīng)用程序?qū)⒈辉试S生成新密鑰或修改現(xiàn)有密鑰。
所有這些都是不錯(cuò)的方式,但很難維護(hù)。管理這問題的一種方法是創(chuàng)建一個(gè)API,API根據(jù)該數(shù)據(jù)庫來驗(yàn)證密鑰。但為此你需要第二個(gè)身份驗(yàn)證系統(tǒng)來連接到這個(gè)API。
不僅檢索API密鑰很繁瑣,維護(hù)其持續(xù)時(shí)間和授權(quán)級(jí)別也是冗長(zhǎng)的過程,而且并不是每個(gè)應(yīng)用程序都在應(yīng)用程序級(jí)別運(yùn)行,它們需要用戶級(jí)別的權(quán)限。
此外,API的使用方式因系統(tǒng)而異。更糟糕的是,不同的應(yīng)用程序共享API密鑰,因此要依賴不同的應(yīng)用程序來維護(hù)對(duì)共享API密鑰的正確訪問級(jí)別。
切換到JWT
任何需要身份驗(yàn)證的API都可以輕松切換到JWT的授權(quán)。通過JWT授權(quán),你可以獲得基于用戶的身份驗(yàn)證。一旦用戶通過身份驗(yàn)證,用戶就會(huì)獲得一個(gè)可以在所有系統(tǒng)上使用的安全令牌。
用戶(以及令牌)采用集中方式的管理。你設(shè)置訪問權(quán)限,并為每個(gè)系統(tǒng)賦予每個(gè)用戶不同的權(quán)限。JWT授權(quán)端點(diǎn)對(duì)用戶進(jìn)行身份驗(yàn)證并創(chuàng)建令牌。
使用這種架構(gòu),下一步就是在整個(gè)生態(tài)系統(tǒng)中創(chuàng)建一個(gè)單點(diǎn)的登錄,并只依賴令牌來獲得權(quán)限。
最后,最簡(jiǎn)單、最穩(wěn)健、最易管理的方法是創(chuàng)建一個(gè)專門用于身份驗(yàn)證和授權(quán)的單一端點(diǎn),這樣整個(gè)生態(tài)系統(tǒng)中的所有其他服務(wù)器都可以依賴該中心點(diǎn)來授權(quán)客戶端和服務(wù)器之間的API交互。
總之,有時(shí)JWT是絕對(duì)必要的,有時(shí)則是“過猶不及”
作為一名應(yīng)用程序開發(fā)人員,我在構(gòu)建API時(shí)主要關(guān)心它們是否得到了正確的使用,并提供了正確的數(shù)據(jù)和功能。
因此,我在很大程度上遵從DevOps的想法來推薦最佳和最易管理的安全措施。
但是,除了安全之外還有其他問題需要關(guān)注。在一個(gè)大型的生態(tài)系統(tǒng)中,我們需要一種簡(jiǎn)單而穩(wěn)健的方式來跨多個(gè)系統(tǒng)和服務(wù)器訪問微服務(wù),而這最好通過集中身份驗(yàn)證和授權(quán)過程來實(shí)現(xiàn)。
在以下兩種情況下,選用JWT顯得有些“過猶不及”:
- 一個(gè)只有幾個(gè)API的簡(jiǎn)單生態(tài)系統(tǒng)。
- 第三方API的供應(yīng)商。
第三方API一般易于使用和快速實(shí)現(xiàn),集成時(shí)只需要很少的前期工作。
此外,你需要共享用于簽名令牌的密鑰。所以最好控制兩端,而這在第三方場(chǎng)景中是不可能的,并且你還需要信任實(shí)現(xiàn)。例如,API可以選擇忽略過期日期或有效起始日期(“not before”)。
不過,JWT 在以下情況就很有必要了:
- 當(dāng)多個(gè)服務(wù)需要在一個(gè)龐大的網(wǎng)絡(luò)上相互通信時(shí),你需要使用JWT。
- 當(dāng)不僅僅是應(yīng)用程序,而是每個(gè)網(wǎng)絡(luò)或應(yīng)用程序都需要基于用戶的不同訪問級(jí)別時(shí),集中和保護(hù)這些交易所至關(guān)重要。
- 控制流量并能夠?qū)W(wǎng)絡(luò)呼叫進(jìn)行優(yōu)先級(jí)排序也很關(guān)鍵。在添加新的或改進(jìn)現(xiàn)有服務(wù)時(shí)最好能獲得簡(jiǎn)單的即插即用體驗(yàn)。
譯者介紹
張?jiān)霰螅?1CTO社區(qū)編輯,多年的安全攻防從業(yè)經(jīng)驗(yàn),主要研究方向:安全開發(fā)、逆向破解、漏洞挖掘、黑灰產(chǎn)攻防對(duì)抗。目前主要從事游戲安全的研究。本人獨(dú)立運(yùn)營(yíng)《小道安全》公眾號(hào)。
原文標(biāo)題:??How API Keys Compare to JWT Authorization: A Detailed Overview??,作者:Algolia