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