從五個方面入手,保障微服務應用安全
隨著計算機、互聯網技術的飛速發展,信息安全已然是一個全民關心的問題,也是各大企業非常重視的問題。企業一般會從多個層次著手保障信息安全,如:物理安全、網絡安全、系統安全(主機和操作系統)、應用安全等。
對于應用程序安全,需要在應用架構、代碼、運維、管理等多個角度進行安全性評估,在整個應用程序生命周期中,軟件工程師們則主要負責身份驗證、訪問授權、進程間通信安全、代碼安全、安全的管理與審計這五方面的方案落地。這五個方面中,前三個側重于技術實現,代碼安全、管理與審計則更需要規范的管理和執行,本文將著重對認證、授權、通信等技術相關內容重點介紹,管理規范相關內容僅做簡單說明。
文中以采用了微服務架構的應用程序為背景進行描述,但多數的應用程序的安全方案與是否采用微服務架構并沒有強關聯,如有差異的地方,文中會提出來。文中描述過程中會涉及到一些微服務架構中相關的概念名詞,說明如下:
微服務架構中安全訪問相關的簡化版的運行視圖如下:

運行視圖
圖中星號*標注的位置就是服務調用過程中安全訪問過程中的一些需要認證鑒權的關鍵位置,如:內外部訪問認證、令牌驗證與授權、內外網通信協議等。后續章節將對這部分展開分析。
1.身份認證
多數業務功能類的應用的首要任務就是需要做身份認證。對于數據公開或新聞發布類的門戶網站類應用不需要考慮這一點,他們更關注的是數據開放之前的管理和審批。
身份認證或身份驗證(Authentication),顧名思義就是對應用程序的"訪問者"的身份進行驗證識別。訪問者分兩類。
- 基于用戶登錄的客戶端(Login-based Client):用戶訪問服務提供者的應用程序的功能時,需要通過一個客戶端交互界面來與服務提供者交互,用戶需要先登錄,然后由客戶端代表用戶身份去訪問服務提供者應用程序。
- API 客戶端(API Client):客戶端程序類型的訪問者,這類客戶端自身具備部分API的訪問權限,不需要用戶授予其訪問權限。
1. 使用認證管理系統IAM進行訪問者注冊認證
不論是用戶還是API客戶端,在訪問應用之前,均需要先到認證管理系統IAM進行注冊,以創建其的身份憑證(用戶賬號和密碼、客戶端ID和密碼)。有了身份憑證,才能通過認證服務的驗證。
統一認證管理系統IAM(Identity and Access Management )負責身份識別和訪問管理,其核心業務是應用系統的訪問者的注冊、賬號密碼憑證、訪問者基本信息的管理、對已注冊的訪問者進行合法性認證和訪問的初級授權(獲取訪問者自身數據)等等。
有些企業還會將組織機構、角色甚至業務功能權限數據也一并歸入IAM系統管理。IAM對權限管理范圍可大可小,不同企業的管理需求和力度也不一樣,需要集中進行功能授權管理還是下放到業務系統中自治各有優缺點,選擇合適自己的方式就好。
2. IAM認證管理系統使用OAuth2.0進行訪問者授權
傳統WEB應用對于用戶登錄訪問,采用會話狀態在服務端保存的方案,用戶請求通常采用會話粘滯(Sticky session)或會話復制(Replication session)策略,來保持客戶端和服務端的會話。為了會話共享而不得不將會話信息寫入公共緩存或數據庫,導致微服務應用之間產生了耦合性。
微服務架構中不推薦采用服務端保存會話的方式,如果引入狀態管理不是必要的,那么應用盡量保持無狀態運行。推薦使用另外一種基于訪問令牌的模式,這種模式下應用中不需要保存會話狀態,并且API客戶端和基于登錄的客戶端均方便使用訪問令牌。微服務架構推薦使用OAuth2.0 授權協議來搭建IAM系統。Spring 體系可以基于Spring Security OAuth實現授權服務器和客戶端。
在本文的上下文中,面向的是企業基于微服務的總體架構進行方案設計,企業整體架構中,默認認證體系方案為企業統一認證而非業務系統各自認證(此方案的前提條件)。OAuth2.0本身是為三方授權而設計的,而在本方案中討論的是企業內部應用的整體認證和授權,不存在第三方。因此本方案中基于OAuth2.0實現的授權服務可以簡單理解為僅為IAM統一認證管理系統中的“賬號管理應用資源提供者”做授權,并且默認實現為認證通過自動授予已登錄賬號數據的讀寫權限,不在登錄通過后與用戶交互確認是否同意授權。其他業務系統作為資源提供者的授權則是系統管理員預置好的授權,也不需要由用戶登錄時決定是否授權。這個OAuth2.0的使用場景可能與其他OAuth2.0相關資料或授權框架默認實現有所不同,請大家注意區分。
OAuth協議中定義了四種角色:
- 資源所有者
能夠許可對受保護資源的訪問權限的實體。當資源所有者是個人時,它被稱為最終用戶。
- 資源服務器
托管受保護資源的服務器,能夠接收和響應使用訪問令牌對受保護資源的請求。
- 客戶端
使用資源所有者的授權代表資源所有者發起對受保護資源的請求的應用程序。術語“客戶端”并非特指任何特定的的實現特點(例如:應用程序是否是在服務器、臺式機或其他設備上執行)。
- 授權服務器
在成功驗證資源所有者且獲得授權后頒發訪問令牌給客戶端的服務器。
角色分析:
- 對于前面提到的API 客戶端,自身具備API訪問權,不需要用戶授權,因此在OAuth角色對應時,它既是客戶端又是資源所有者。
- 微服務架構中Web應用一般采用前后端分離的模式,前端為基于瀏覽器訪問的純前端應用,網關作為應用程序的入口,此時網關本身可以代表OAuth中的客戶端身份訪問服務提供端應用的功能接口。
- 在微服務架構中,負責頒發訪問令牌的授權服務通常在IAM系統中實現
- 資源服務器,在微服務架構中,所有的業務系統中的服務功能提供者都是資源服務器,也包括IAM系統的賬號、組織機構服務、資源權限管理服務等等
在OAuth2.0授權協議中,主要定義了四種許可類型:授權碼許可、簡單許可、密碼憑據許可和客戶端憑據許可,詳細請參見規范內容:rfc6749 - The OAuth 2.0 Authorization Framework
(https://tools.ietf.org/html/rfc6749)
下面我們根據訪問者類型,分別推薦合適的授權方案。
2.1 API客戶端作為訪問者,使用客戶端憑證許可
典型的API客戶端如批量調度系統、物聯網設備程序等,通常不需要用戶登錄授權就可以自動運行。使用客戶端憑證許可類型比較適合。

客戶端憑證
上圖為OAuth2.0規范標準流程圖,結合此場景中,對應OAuth2.0中的角色,API客戶端作為OAuth2.0的客戶端、IAM則為授權服務器。
- (A) API客戶端與授權服務器IAM進行身份驗證并請求訪問令牌。
- (B) 授權服務器IAM對API客戶端進行身份驗證,如果有效,頒發訪問令牌。客戶端存儲訪問令牌,在后 續的請求過程中使用。如果令牌過期或失效或需要重復此流程再次申請訪問令牌。
其他說明:
- 此類應用通常不具備與用戶交互的UI,無需用戶授權,具備保證客戶端憑證安全的能力
- 此類應用后端需要具備通過https訪問授權服務器的能力
- 此類應用或設備需要具備存儲Access Token的能力
2.2 基于登錄的客戶端作為訪問者,使用授權碼許可
2.2.1 Web 應用
OAuth2.0 協議中提出前端單頁Web應用可以用簡單許可模式,但簡單許可模式有些局限性,令牌到期就需要重新登錄授權,不支持令牌刷新,用戶體驗較差。很多使用簡單授權的應用為了改善用戶體驗會頒發一個長期的令牌幾天甚至幾周。
如果有條件使用授權碼模式,支持刷新令牌則是一個更好的選擇。對訪問令牌時間較短如2分鐘,刷新令牌為一次性令牌有效期略長如30分,如果存在已作廢的刷新令牌換取訪問令牌的請求,授權端點也能夠及時發現做出相應入侵處理,如注銷該用戶的所有刷新令牌。在保持良好用戶體驗的同時還兼顧了部分安全性。
本場景以微服務架構中常見的前后端分離Web應用作為示例,前端是單頁應用,網關作為Web后臺是服務提供端應用功能入口,也可作為OAuth2.0的客戶端,讓前端Web應用能借助網關實現授權碼交換。因此在微服務架構中,即便是純前端單頁應用類的Web應用,仍可以用基于網關交互的授權碼模式獲取訪問令牌。其他非前后端分離的混合Web應用自身就是客戶端,不需要借助網關交換訪問令牌。

授權碼
上圖為OAuth2.0規范標準流程圖,結合此場景對應OAuth2.0中的角色,用戶是資源所有者、瀏覽器為用戶代理、網關作為被授權的客戶端、IAM則為授權服務器。
- (A) 網關通過引導瀏覽器開始流程授權流程,重定向到統一認證中心的登錄頁面。
- (B)用戶輸入密碼登錄,授權服務器驗證用戶身份,并確認用戶是否授權網關的訪問請求。
- (C)用戶授權后,認證中心根據之前網關注冊時提供的回調地址,引導瀏覽器重定向回到網關。重定向URI包含授權碼
- (D)網關通過包含上一步中收到的授權碼和網關自身憑證從授權服務器IAM的請求訪問令牌。
- (E)授權服務器IAM對網關進行身份驗證,驗證授權代碼,并確保接收的重定向URI與網關注冊時的URI相匹配。匹配成功后,授權服務器IAM響應返回訪問令牌與可選的刷新令牌給網關。
其他說明:
- 為了前端會話保持,訪問令牌由網關在響應時返回到前端,存儲到前端存儲空間,如Cookie、Local Storage、Session Storage等。
- 訪問令牌失效后,網關根據自己的客戶端憑證+刷新令牌一起發送授權服務器,獲取新的訪問令牌和刷新令牌,并再返回響應中將訪問令牌寫入到用戶瀏覽器的存儲中。
- 授權碼模式中,用戶的憑證(用戶名、密碼)是用戶通過瀏覽器與授權服務交互,并不經過網關, 安全性最好。
2.2.2 移動App
個人移動設備上安裝的原生App本身具備實現授權碼流程的能力,不需要借助網關實現授權碼流程。認證通過后網關僅負責校驗訪問令牌即可。

授權碼
移動App實現登錄重定向通常可以采用如下方式:
- 模擬web端,使用移動端瀏覽器與WebView
- 使用URI scheme與Intent在應用間跳轉
移動端App的運行環境是不可信的,容易被惡意App入侵的風險,包含如下兩種情況:
- 重定向過程中的回調URL參數,容易被惡意App占用URL Scheme或者監聽localhost端口截取。
- 運行環境不可靠,移動App不具備安全保存客戶端秘鑰的能力,而使用授權碼獲取訪問令牌時需要校驗客戶端秘鑰。
基于上述風險和問題,移動App基于授權碼獲取訪問令牌的流程需要進行優化解決,rfc規范中建議的實現方案是移動App授權流程采用使用帶有PKCE支持的授權碼模式。PKCE, 全稱Proof Key for Code Exchange,即保護授權代碼授權。這其實是通過一種密碼學手段確保惡意第三方即使截獲Authorization Code或者其他密鑰,也無法向認證服務器交換Access Token。
經過PKCE改進的授權碼、訪問令牌交換過程示意圖如下:

PKCE
上圖為PKCE模式下授權碼申請和交換訪問令牌的過程,說明如下:
- (A) 移動App客戶端創建并保存名為code_verifier的隨機秘鑰串,并將秘鑰字符串加密轉換的結果t(code_verifier)稱為code_challenge,將轉換后的code_challenge以及轉換方法一并發送到授權服務中。
- (B) 授權服務返回授權碼并記錄code_challenge和轉換方法t_m。
- (C) 移動App客戶端收到授權碼后,將授權碼和code_verifier秘鑰串發送到授權服務器,用以申請訪問令牌。
- (D) 授權服務器根據轉換方法t_m 轉換code_verifier 并與步驟A中收到的code_challenge比較,如果一致則返回訪問令牌,否則拒絕非法請求。
第三方無法根據code_challenge推導出code_verifier,因為code_challenge采用了不可逆加密方式。只有移動App客戶端自己才知道這兩個值。因此即使惡意App截獲了code_challenge和授權碼,也無法換取訪問令牌避免了安全問題。要實現這種移動App的PKCE授權碼模式,出移動App自身外,還需要IAM的授權服務器基于標準的授權碼流程擴展配合實現。
關于移動App安全認證的詳細內容請參考官方規范:
- rfc8252 - OAuth 2.0 for Native Apps
(https://tools.ietf.org/html/rfc825)
- rfc7636 - Proof Key for Code Exchange by OAuth Public Clients
(https://tools.ietf.org/html/rfc7636)
2.3 高度信任的特權類客戶端,可以使用資源所有者密碼憑據許可

用戶密碼憑據
上圖為OAuth2.0規范標準流程圖,結合此場景中,對應OAuth2.0中的角色,用戶是資源擁有者、特權應用是客戶端、IAM提供授權服務器
- (A)用戶提供給特權App用戶名和密碼。
- (B)特權App將用戶憑據提交給授權服務器IAM,申請訪問令牌
- (C)授權服務器IAM 驗證用戶的憑證,如果有效,頒發訪問令牌給特權App。特權App對授權服務器頒發的訪問令牌、刷新令牌進行存儲和更新。
其他說明:
- 雖然是特權App,但App中不要持久化保存用戶密碼,僅登錄時使用
- App負責保存Access Token 、Refresh Token
3. 使用API 網關作業務系統訪問入口,負責驗證訪問令牌
訪問者能夠訪問的接口通常是兩類:身份認證API、應用功能類API。
- 身份認證類API:即登錄認證相關的API。為了避免用戶、客戶端憑證泄漏第三方(除IAM、訪問者之外為第三方),身份認證類API或UI建議由IAM系統直接開放給訪問者調用進行身份認證。
- 應用功能類API:功能實現來自服務提供者,通過網關開放給訪問者。網關是訪問應用API的入口。
用戶登錄認證由IAM授權服務器配合用戶資源服務負責。認證成功后,IAM訪問者頒發訪問令牌。后續對應用功能的訪問過程中,均須攜帶訪問令牌以表明訪問者的身份。
3.1 由網關負責客戶端身份驗證
網關作為業務系統的API入口,當面向外網的訪問者時網關還是內外網的分界,訪問令牌驗證理應由網關負責,不應該將令牌驗證的事情交給服務提供者。網關負責驗證既能避免未經驗證的請求進入內網,又能夠簡化服務提供端的代碼,服務提供端無需處理不同類型客戶端的驗證。實際上好處還不只這些,在網關可以統一做流控無需應用端重復建設類似功能;系統內部調試、變更敏捷,減少了跨組織交互。
網關驗證訪問令牌有兩種方案:網關委托認證服務驗證、網關直接驗證,說明如下:
- 方案一:網關委托授權服務驗證,每次收到請求后,網關均將訪問令牌發送到IAM認證服務進行認證,認證通過后才允許繼續訪問。

網關委托IAM校驗令牌
- 客戶端成功認證后,使用UUID類型的訪問令牌調用網關上的服務
- 由于UUID類型令牌不包含客戶端的信息,網關需要委托IAM認證服務校驗令牌
- 令牌檢查合法后,將請求路由到服務提供者
- 應用中也無法解析令牌,需要根據UUID令牌到IAM中獲取用戶信息
- 方案二(推薦):網關直接驗證,要求網關能識別IAM頒發的令牌,這種模式推薦用 JWT令牌,網關需要具備解析校驗JWT加密的訪問令牌的能力。

網關直接校驗令牌
- 客戶端成功認證后,使用JWT令牌調用網關上的服務
- 網關自己直接解密JWT令牌進行校驗
- 令牌檢查合法后,將請求路由到服務提供者
- 應用受到請求后,如果需要更多權限信息,如果可以根據Token去權限管理服務獲取權限信息(非必須步驟,需要時添加)。
上述兩方案中,方案一的令牌是無業務含義的身份標識字符串,每次收到請求網關都去IAM認證,對IAM認證服務的性能壓力較大。方案二中IAM頒發的令牌中包含部分客戶端或用戶信息,使用JWT加密,IAM將驗證方式或SDK提供給了負責認證的網關。對于IAM來說,減少了每次請求令牌認證帶來的通信次數,減輕了IAM的壓力。
推薦采用方案二實現令牌檢查,需要注意的是方案二中的JWT令牌中僅包含必要的信息即可,不要放太多的角色權限信息。后續功能中需要額外的信息時,可以根據令牌再去IAM中獲取。如果令牌中存放了很多的權限數據,一旦后臺的授權數據發生變化,令牌中的權限數據與實際IAM的權限會存在不一致的問題,只能強制用戶下線重新登錄。
JWT令牌是防篡改的,但并不加密,如需要存儲到瀏覽器存儲中,建議采用JWT+JWE方式進行令牌加密。令牌中存放必要少量數據即可,避免濫用。多數服務器通常會對Http header、cookie長度做限制。
3.2 系統內部應用是否通過網關?
我的答案是不需要,否則太麻煩了。通常網關是獨立團隊負責,API變更發布、內部聯調驗證還得跨團隊協調實在不可行。推薦系統內直通不走網關,系統之間訪問必須走網關。要做到這一點,應用也需要實別請求來源進行客戶端認證,這種認證方案沒必要太復雜,應用只應該允許信任的網關和系統內部應用程序訪問其服務,不允許系統外部請求繞過網關直接調用,因此,需要在網關和系統內部應用之間這個小范圍內建立信任,常見方案有兩種:
- 方案一,內部令牌:系統內的應用在發布接口到網關時,提供一個系統內部共享的令牌給網關和系統內所有應用,接收到請求時檢查請求頭中是否包含系統內信任的令牌, 如果包含可信任令牌,那么就允許訪問,否則就拒絕
- 方案二,系統內保密令牌+網關證書單獨認證:系統內用保密令牌交互就是方案一,只是內部令牌不共享給網關,網關用公私鑰證書簽名方式與域內系統建立信任,由網關生成公私鑰證書,頒發公鑰給各個系統,網關調用服務提供者時,請求頭中帶上用私鑰簽名的令牌,應用收到請求以后用網關發布的公鑰驗證其令牌。
方案一優點是實現簡單,缺點是安全級別略低,常見的企業架構中,網關和業務系統會是不同團隊甚至不同的廠商負責開發維護,內部令牌共享給了其他團隊負責的網關,存在一定的風險。方案二相比方案一略復雜一點,安全性更高,系統內互通用內部令牌,系統和網關認證使用了網關提供的安全令牌檢查方式。兩種方案可根據實際需求選擇。
2.訪問授權
通過認證的API客戶端能夠訪問網關開發的所有API嗎?通過認證的用戶能夠調用所有API嗎?通過認證的用戶允許調用修改訂單的接口,那么他能修改所有人的訂單嗎?
很顯然絕大多數場景下上述三個問題答案都是"不能"。在絕大多數業務場景中除了對訪問者的身份認證之外,我們還需要再進一步控制權限。
1. API客戶端訪問網關接口時,網關需進行API權限控制
如果訪問者是API客戶端時,API調用的權限需由網關進行控制。建議采用先訂閱再訪問的授權模式,網關應該僅允許API客戶端訪問其訂閱過的API 。具體實現方法就是絕大多數網關都會提供的基于API Key控制API訪問的方式。
需要注意的是,僅使用API key的訪問控制是不夠的。API Key是在網關訂閱API時生成的一串唯一編號,并不具備識別客戶端身份的能力。就好比以前買火車票是不實名的,誰拿到火車票,都可以乘坐對應車次。火車票實名制之后,首先需要核驗身份證,核驗通過后才能購票乘車。如果證票不符,則不允許乘車。
將客戶端認證和API Key配合進行訪問認證和權限校驗才是個更安全的方案。

API權限控制
上圖為訪問令牌結合API Key的認證鑒權示意圖,說明如下:
- 客戶端1獲取了API Key 但其沒有合法的訪問令牌,如果不允許匿名訪問,則網關會拒絕客戶端1訪問,返回錯誤碼401表示客戶端未通過認證;
- 客戶端2擁有了合法的訪問令牌,但其API Key不合法,網關在客戶端2認證檢查通過后,檢查API Key,發現其權限不足,則返回錯誤碼403表示客戶端的權限不足;
- 客戶端3擁有合法的客戶端訪問令牌和API Key訪問網關上的服務,網關認證、鑒權通過之后,將請求路由到實際的服務提供端,最終發回正常響應數據。
2. 用戶訪問應用功能時需要進行權限控制
用戶訪問的功能權限或數據權限不要交給網關管控,原因是網關僅能支持API Path授權,而實際需要控制的用戶權限有很多,如菜單、API、數據等。如果由網關控制用戶權限,管少了不滿足需求,管多了就要耦合太多應用數據。
因此推薦用戶權限由業務系統自行管理維護和控制。每個業務系統內部如果需要控制用戶權限,可以建設一個基礎權限框架,負責管理權限數據,并提供訪問請求攔截和權限檢查的SDK給其他應用。
也有部分企業權限管理要求較高,將系統內部的基礎權限框架抽取為獨立的權限管理服務,由獨立團隊維護該服務,采用分布式部署+權限緩存的方式保障性能。這樣做的好處是權限模型統一、容易對權限變更進行審批控制和審計。缺點是跨團隊交互,變更流程復雜。
關于權限管理,是業務系統自治或是集中管控,根據企業自身的需求特點決定即可。
3.通信安全
通信安全的方案就是基于傳輸過程加密的方式,常見的選擇就是使用Https協議通信。
微服務架構體系中,邏輯層面上外部請求接入都是通過網關作為入口,網關作為內外網的分界,實際部署上,網關本身也是多實例分布式的高可用部署形態,前面架設有一個負載均衡F5或nginx,用來對外提供Https協議接入和路由轉發,而網關內部就是企業內網,默認是可信任的,內網的系統之間的通信會采用更輕量級的HTTP協議。此方案中微服務換成SOA,把網關換成ESB,就是傳統的SOA架構中的安全通信方案,本質上沒有區別。
示意圖如下:

內外網通信協議
為什么用了https就能保證通信安全呢?https是http+ssl,采用密碼學手段對通信報文做了加密,使得報文無法被篡改,做到了安全傳輸,從而保障了通信安全。關于https原理和負載均衡器證書配置相關資料網絡上有很多,請大家即用即查。
4.代碼安全
敏感配置加密:上述各種服務安全場景和方案聊了那么多,大家發現保存好令牌、密鑰、密碼是一切安全的前提。這些東西千萬不能外泄。要保證密碼不泄露的辦法就是做好敏感數據保密,技術手段上則要求存儲密碼、憑證的地方(配置文件和數據庫表)需要加密存儲。如:配置文件中的數據庫口令、數據表中存放的密碼數據等
代碼質量管理:建議在開發期對于編碼規范進行制定,還可以通過工具進行輔助檢查和控制,如開源的代碼質量管理工具Sonar,可以支持多種程序語言,方便的與編譯構建工具集成如Maven,在代碼進入正式提交對應分支前就將一些安全問題在前期預防,如SQL注入等。
運行時安全掃描:測試階段,可以通過安全掃描軟件,如AppScan 等工具對業務系統的前后端功能進行掃描,檢查系統漏洞并即時修復。盡量減小在上線運行后出現安全事故的風險。
5.管理審計
運維管理安全方面,根據安全需求,要有安全相關的管理規范和工具支撐,對系統管理、權限分配和關鍵數據進行嚴格管控,并做好操作審計日志記錄。
比如很多安全級別高的行業或企業中如軍工類,對于業務系統的修改、權限管理審計做了嚴格的流程規范和功能支撐。如典型的三員管理,采用三權分立、互相制約的思路,包含系統管理員、安全管理員、安全審計員三個角色,互相能看到對方的信息,將業務過程分成不同的段,每段由對應人員負責,不讓任何人掌控全局。
審計工作是非常重要的一環,沒有任何系統和流程是絕對安全的。關鍵的操作、數據變化等審計日志需要完整記錄。一旦發生問題,可以通過審計日志排查分析追蹤。常見內容舉例如下:
- 對于敏感數據項(如:密碼)的訪問
- 客戶端注冊、用戶認證授權過程
- 權限的授予和廢除
- 關鍵數據的變更、刪除
- 審計功能的啟動和關閉
- 其他關鍵API、命令的訪問
以上這些審計方面的工作中,如果是基于API相關的審計信息記錄,如邊界交互報文數據,建議基于統一的技術框架進行記錄管理。一些內部實現方法,則可以采用接口、方法上加注解,AOP攔截后記錄的方案。其他情況可根據實際需求設計審計數據存儲的方案。


























