項(xiàng)武銘,鮑亮,俞少華
(公安部第三研究所,上海200031)
隨著Web 應(yīng)用規(guī)模的逐漸增大,單服務(wù)器無(wú)法滿足業(yè)務(wù)需求,分布式服務(wù)器架構(gòu)成為發(fā)展的必然趨勢(shì)。從LAMP 到資源分類的服務(wù)器集群,其原有的服務(wù)模式也需要隨之改進(jìn)。以Web 應(yīng)用的驗(yàn)證方式為例,傳統(tǒng)的cookie-session 模式是基于有狀態(tài)服務(wù)器的驗(yàn)證,即服務(wù)器上保存用戶的登錄session,用戶發(fā)送請(qǐng)求時(shí)附帶服務(wù)器簽發(fā)的cookie,服務(wù)器根據(jù)存儲(chǔ)的session 內(nèi)容與cookie 對(duì)比來(lái)驗(yàn)證用戶的身份。在分布式服務(wù)器架構(gòu)中,用戶請(qǐng)求可能就會(huì)由不同服務(wù)器進(jìn)行處理,必然會(huì)出現(xiàn)session 如何獲取的問(wèn)題,在這個(gè)問(wèn)題上,目前也有一些解決方案,例如采用粘性session,所有服務(wù)器同步session,用Redis 緩存session 以及將session 持久化到本地再讀取,等等[1]。而對(duì)于采用HTTP 無(wú)狀態(tài)設(shè)計(jì)理念的RESTful API 來(lái)說(shuō),token 驗(yàn)證模式更加適應(yīng)前后端分離的分布式應(yīng)用的驗(yàn)證,它可以不用在服務(wù)器端存儲(chǔ)狀態(tài),用戶登錄以后,請(qǐng)求中攜帶服務(wù)器加密生成的token,服務(wù)端驗(yàn)證token 的有效性來(lái)判斷用戶是否已經(jīng)登錄。驗(yàn)證環(huán)節(jié)相比于session模式節(jié)省了多服務(wù)器之間的認(rèn)證信息交換,在網(wǎng)絡(luò)流量安全上也有一定程度的提升。
在目前的token 驗(yàn)證方式中,越來(lái)越多的是使用JWT 驗(yàn)證,它基于JSON 格式數(shù)據(jù)并采用加密算法進(jìn)行簽名。由于JWT 是帶簽名的token,其攜帶數(shù)據(jù)在一定程度上可以作為可信來(lái)源[2],服務(wù)器對(duì)于用戶信息不經(jīng)常修改且需要多次查詢的部分內(nèi)容放入JWT 的載荷中,可以降低對(duì)該部分?jǐn)?shù)據(jù)庫(kù)的查詢頻率,減少網(wǎng)絡(luò)中流量碎片。而對(duì)于角色權(quán)限驗(yàn)證,傳統(tǒng)的方式多為與用戶驗(yàn)證分離,往往是通過(guò)用戶ID 去查表得到用戶角色,再根據(jù)用戶角色查表得到操作權(quán)限。順應(yīng)RESTful 以資源為主的理念,本文對(duì)RESTful API 的設(shè)計(jì)中嵌入權(quán)限訪問(wèn)控制,利用JWT 攜帶權(quán)限信息實(shí)現(xiàn)用戶的權(quán)限驗(yàn)證。
JWT(JSON Web Token)是由國(guó)際互聯(lián)網(wǎng)工程任務(wù)組(IETF)提出的一個(gè)行業(yè)標(biāo)準(zhǔn)(RFC 7519)。在官方簡(jiǎn)介中,JWT 被定義為在通訊雙方之間一種緊湊和安全的傳遞協(xié)議,其原始聲明可以是一個(gè)JWS(JSON Web Signature)結(jié)構(gòu)來(lái)描述的載荷,也可以是采用JWE(JSON Web Encryption)方式進(jìn)行加密的明文數(shù)據(jù)。標(biāo)準(zhǔn)的JWT 采用三段式的聲明,用英文點(diǎn)號(hào)(.)進(jìn)行分段,內(nèi)容依次為頭部信息(Header)、載荷信息(Payload)以及簽名(Signature),典型形式如aaaaa.bbbbb.ccccc。其中頭部信息給出了該token 的類型以及加密或簽名的算法,載荷信息部分可以使用預(yù)置的聲明,例如發(fā)行者、過(guò)期時(shí)間、主題、接受者,等等,也可以使用公共或者私人定制的聲明,這兩個(gè)部分均以Base64Url 進(jìn)行加密。簽名部分則是使用頭部信息中指定的算法將編碼后的頭部、載荷以及服務(wù)端密鑰進(jìn)行加密,形成簽名,如圖1 所示[3]。
圖1 HSA256算法簽名形成的JWT
其驗(yàn)證流程也十分簡(jiǎn)明,客戶端使用用戶憑據(jù)登錄系統(tǒng),服務(wù)器驗(yàn)證通過(guò)后,依據(jù)上述規(guī)則生成jwt 返回給客戶端??蛻舳酥笤谙蚍?wù)器請(qǐng)求時(shí),通過(guò)header 中的Authorization 字段以Bearer 形式攜帶此token 來(lái)發(fā)送至服務(wù)器端驗(yàn)證身份和權(quán)限。一般的token流程可以由圖2 來(lái)表示,申請(qǐng)為1~2 步驟進(jìn)行,請(qǐng)求資源以3~6 步驟進(jìn)行。
圖2 Token驗(yàn)證過(guò)程
在安全要求等級(jí)較高的應(yīng)用系統(tǒng)中,用戶的權(quán)限會(huì)被嚴(yán)格劃分,并且按照最小需求權(quán)限進(jìn)行分配可操作資源,并且系統(tǒng)一般會(huì)禁用或者不存在最高權(quán)限的用戶。例如,網(wǎng)絡(luò)安全等級(jí)保護(hù)要求下的三員設(shè)置,存在系統(tǒng)管理員,安全保密管理員以及安全審計(jì)員等標(biāo)準(zhǔn)角色,各個(gè)角色的不能互相訪問(wèn)其對(duì)應(yīng)的資源。將JWT 應(yīng)用于RESTful API 權(quán)限進(jìn)行驗(yàn)證,需要對(duì)資源對(duì)象的訪問(wèn)規(guī)則進(jìn)行定制以及對(duì)JWT 進(jìn)行內(nèi)容和加密方式改進(jìn)。
以上述系統(tǒng)為例,在對(duì)各類資源API 進(jìn)行操作需要鑒定請(qǐng)求者的資源訪問(wèn)權(quán)限時(shí),可以參照強(qiáng)制訪問(wèn)控制方式(Mandatory Access Control,MAC),從資源對(duì)象(URL)角度進(jìn)行權(quán)限分配,對(duì)不同的資源對(duì)象設(shè)置可訪問(wèn)角色以及對(duì)應(yīng)操作權(quán)限[4]。對(duì)應(yīng)于資源的增刪改查,請(qǐng)求的方式method 也依據(jù)RESTful 設(shè)計(jì)規(guī)范分別用post、delete、put、get 來(lái)定義。例如對(duì)某一資源events 的訪問(wèn)規(guī)則策略可以用以下方式描述:
上述規(guī)則也是按照J(rèn)SON 格式來(lái)定義,一級(jí)屬性字段描述了對(duì)應(yīng)的API 接口,其值為策略對(duì)象,內(nèi)部屬性為用戶角色和對(duì)應(yīng)的權(quán)限數(shù)組。上圖中,events 資源可以被系統(tǒng)管理員生成和查看,安全保密管理員僅可以查看,審計(jì)員可以查看和刪除;針對(duì)某一具體event 資源,該資源創(chuàng)建者具有查看,更新以及刪除權(quán)限,系統(tǒng)管理員和安全保密員具有查詢權(quán)限,審計(jì)管理員具有查看和刪除權(quán)限,而未授權(quán)者沒(méi)有任何權(quán)限。
資源服務(wù)器對(duì)接收的請(qǐng)求先讀取資源對(duì)象的控制列表,根據(jù)請(qǐng)求角色在列表中所擁有的權(quán)限,判斷是否進(jìn)行資源操作,其角色就以JWT 中信息來(lái)識(shí)別。
在用戶登錄時(shí),權(quán)限驗(yàn)證服務(wù)器對(duì)用戶憑證進(jìn)行數(shù)據(jù)庫(kù)查詢,得到用戶id,用戶角色以及其他基本信息,生成JWT 時(shí)除了用戶id、過(guò)期時(shí)間、刷新時(shí)間等信息之外,還將用戶的角色信息寫入到JWT 載荷中。若有需求臨時(shí)增加該用戶角色之外權(quán)限,權(quán)限驗(yàn)證服務(wù)器還可以再簽發(fā)具有擴(kuò)展權(quán)限的token,短期內(nèi)有效。資源服務(wù)器只驗(yàn)證用戶所提供的JWT 是否有效,讀取其中的用戶角色和臨時(shí)權(quán)限信息判斷是否提供對(duì)應(yīng)資源。相比于傳統(tǒng)token 驗(yàn)證,減少了用戶角色和權(quán)限查表的次數(shù),僅需要在申請(qǐng)JWT 時(shí)做一次查詢。
圖3 基于JWT的用戶權(quán)限驗(yàn)證流程
標(biāo)準(zhǔn)JWT 格式中的載荷部分是由Base64Url 方式加密,具有不可讀性,但不具備保密性;簽名部分是可選簽名算法,一般在頭部信息中指定。在一些安全要求更高的場(chǎng)合,JWT 載荷部分信息也需要加密處理,可以采用非對(duì)稱加密對(duì)該部分信息進(jìn)行加密,同樣可以在頭部信息添加對(duì)應(yīng)字段描述為何種加密算法,這樣用來(lái)保障JWT 本地存儲(chǔ)和讀取過(guò)程中數(shù)據(jù)安全,在通訊過(guò)程中的數(shù)據(jù)安全可以采用HTTPS 協(xié)議方式來(lái)保障。
本案例實(shí)驗(yàn)采用基于Node.js 的Express 框架實(shí)現(xiàn)RESTful API 服務(wù)器,自定義users 和events 兩個(gè)資源項(xiàng),其下各具有多個(gè)實(shí)例對(duì)象,訪問(wèn)控制規(guī)則ACL 作為中間件,取app 為express 的實(shí)例,利用app.use()引入該中間件[5],按以下步驟驗(yàn)證角色權(quán)限:①驗(yàn)證JWT 簽名是否有效,無(wú)效則返回默認(rèn)401 狀態(tài),有效則進(jìn)行下一步;②讀取ACL 規(guī)則,得到JWT 中攜帶的角色在規(guī)則中所限定的權(quán)限;③判斷該用戶訪問(wèn)API 的權(quán)限是否符合規(guī)則。
其判斷權(quán)限部分的具體實(shí)現(xiàn)代碼為:
在圖3 所設(shè)計(jì)的訪問(wèn)規(guī)則下,system administrator角色用戶對(duì)event 資源僅有查詢和新增權(quán)限,其余操作均為禁止,則當(dāng)以用戶system administrator 對(duì)events 資源進(jìn)行增刪改查操作,如圖4 結(jié)果(本例使用IDEA 的mocha 測(cè)試可視化插件),對(duì)號(hào)為成功返回預(yù)期結(jié)果,驚嘆號(hào)為未能返回結(jié)果。其他用戶和不同權(quán)限的情況與本例類似,不再重復(fù)測(cè)試。
圖4 Events資源的RESTful API訪問(wèn)控制測(cè)試結(jié)果
根據(jù)JWT 攜帶的角色權(quán)限信息,使用對(duì)應(yīng)的中間件,對(duì)RESTful API 進(jìn)行權(quán)限驗(yàn)證,對(duì)資源服務(wù)器不同操作權(quán)限進(jìn)行判斷。相比于傳統(tǒng)角色權(quán)限判斷方式,該方式減少了查表過(guò)程,有利于網(wǎng)絡(luò)數(shù)據(jù)傳輸安全,其模塊化插件化的實(shí)現(xiàn)形式,降低了與系統(tǒng)耦合度,非常適應(yīng)于應(yīng)用擴(kuò)展。JWT 的token 驗(yàn)證機(jī)制攜帶的信息簽名能保證數(shù)據(jù)不被篡改,為用戶角色權(quán)限等特征信息提供安全可靠保障,在分布式、大規(guī)模的Web 應(yīng)用中得到廣泛應(yīng)用,基于此基礎(chǔ)設(shè)計(jì)的權(quán)限驗(yàn)證方案也具有一定的應(yīng)用場(chǎng)景。