4A 安全之授權:編程的門禁,你能解開嗎?

概述

在安全管理系統裏面,授權(Authorization)的概念常常是和認證(Authentication)、賬號(Account)和審計(Audit)一起出現的,並稱之爲 4A。就像上一文章提到的,對於安全模塊的實現,最好都遵循行業標準和最佳實踐,授權也不例外。

作爲安全系統的一部分,授權的職責如下:

  • 確保授權過程的可控:常見的參考標準有 OAuth2、SAML2、CAS 等協議
  • 確保授權結果的可控:常見的參考標準有 RBAC、ABAC 等授權模型

對於大多數應用來說,主流的做法是基於 OAuth2 + RBAC 的組合搭配實現授權。下面就從這兩個方向展開聊聊。

RBAC

RBAC(角色基礎訪問控制)是一種常見的權限管理方式。在這種模型中,系統根據用戶的角色來分配權限,而不是直接分配給單個用戶。這樣可以簡化權限管理和配置的複雜性。避免頻繁的對用戶進行權限操作。如下:

sequenceDiagram participant U as 用戶 participant R as 角色 participant P as 權限 participant Rsrc as 資源 U->>R: 請求分配角色 R->>P: 擁有權限 P->>Rsrc: 訪問資源 note right of U: 用戶通過角色獲得權限 note right of R: 角色通過權限訪問資源

如果還有更復雜的訪問控制需求,則可以在 RBAC0 的基礎上可以擴展 RBAC1 (層次化 RBAC,角色之間有繼承關係)和 RBAC2(受約束的 RBAC,角色之間有互斥關係)來提高系統的安全性和管理的便利性。還有 RBAC 3 等等。

對於大多數應用來說,通常都無需自己去實現這些理論的模型,應用遇到的安全問題大多都是相同的,具有普遍性,所以可以抽象到框架層面來解決,例如著名的 Spring Security 框架就提供 RBAC 模型的授權實現。

不過,需要特別說明的是,與具備通用性的訪問控制權限相比,對於數據權限的控制則顯的困難的多,用戶能訪問的數據權限通常與業務高度關聯,具體到不同部門,不同角色,甚至指定人員可以訪問的數據權限都不盡相同。完全不具備通用性,所以無法通過框架層面解決,就連 Spring Security 框架也未能提供數據權限的相關控制。只能有業務系統結合實際情況各自在業務層實現,這也是目前無法解決的問題。

OAuth 2

OAuth2 是一種業界標準的授權協議,允許用戶授權第三方應用程序訪問他們在其他服務提供者上的資源,而無需分享用戶名和密碼,它定義了四種授權交互模式,適用於各種應用場景:

  • 授權碼模式
  • 隱式授權
  • 用戶模式
  • 應用模式

OAuth2 通過發放訪問令牌(Access Token)和刷新令牌來實現對受保護資源的訪問控制。通過創新的使用訪問令牌 Token 替代了用戶密碼,避免用戶憑證的泄露。

授權碼

授權碼模式可以說是最安全的授權模式,綜合考慮了各種風險和防範措置,但相對也是最複雜的授權協議,適合有服務端可以存儲密鑰(ClientSecret)的場景,授權流程如下:

sequenceDiagram participant 用戶 as 用戶 participant 客戶端 as 客戶端應用 participant 授權服務器 as 授權服務器 participant 資源服務器 as 資源服務器 用戶->>+客戶端: 訪問客戶端應用 客戶端->>+授權服務器: 請求授權碼 授權服務器->>+用戶: 請求登錄和授權 用戶->>+授權服務器: 提供憑證並授權 授權服務器->>+客戶端: 返回授權碼 客戶端->>+授權服務器: 使用授權碼請求訪問令牌 授權服務器->>+客戶端: 發放訪問令牌和刷新令牌 客戶端->>+資源服務器: 使用訪問令牌請求用戶數據 資源服務器->>+客戶端: 返回用戶數據

看完授權碼的過程,你可能會覺得好奇:爲什麼授權服務器要返回授權碼,而不直接返回令牌呢 ?

返回授權碼而不是直接返回令牌的設計主要是爲了提高安全性,原因如下:

  1. 即使授權碼被截獲,攻擊者因爲沒有客戶端密鑰無法獲取訪問令牌,客戶端密鑰只在服務器端保存,不會通過前端暴露。
  2. 在重定向回客戶端應用的過程中,授權碼會通過瀏覽器傳輸。如果直接傳輸訪問令牌,一旦泄露,就會帶來更高的安全風險。授權碼則可以進行嚴格的限制(如一次性使用,很短的有效期),所以即使泄露也難以被利用。
  3. 在客戶端使用授權碼請求訪問令牌時,授權服務器可以驗證請求中包含的客戶端密鑰和重定向 URI 等信息,確保令牌的請求合法

另外令牌頒發的策略上,授權碼模式下也使用長刷新令牌 + 短訪問令牌的雙令牌策略,來最大化減少 JWT 令牌無狀態難回收的問題。因此,在服務端可以存儲令牌的前提下,授權碼模式可以說是大多數場景下的首選。

隱式授權

隱式授權模式對於實在沒有服務端存儲 ClientSecret 的純前端應用提供接入支持。接入流程也比較簡單,如下:

sequenceDiagram participant 用戶 as 用戶 participant 客戶端 as 客戶端應用 participant 授權服務器 as 授權服務器 participant 資源服務器 as 資源服務器 用戶->>+客戶端: 訪問客戶端應用 客戶端->>+授權服務器: 請求授權碼 授權服務器->>+用戶: 請求登錄和授權 用戶->>+授權服務器: 提供憑證並授權 授權服務器->>+客戶端: 發放訪問令牌 客戶端->>+資源服務器: 使用訪問令牌請求用戶數據 資源服務器->>+客戶端: 返回用戶數據

該模式下用戶認證通過後授權服務器就直接向客戶端返回令牌,無需應用提供 ClientSecret 和通過授權碼獲取令牌的步驟。但是代價是安全等級降低,令牌有可能在重定向的時候暴露給攻擊者。

爲了挽救安全等級的問題,OAuth 2 也儘可能做了最大的努力,例如:

  1. 限制第三方應用的回調 URI 地址必須與註冊時提供的域名一致
  2. 在隱式模式中明確禁止發放刷新令牌
  3. 令牌必須是 “通過 Fragment 帶回” 的(意味着只能通過 Script 腳本來讀取,具體參考 RFC 3986)

可以看到隱式授權已經盡最大努力地避免了令牌泄漏出去的可能性。也並非主流的 OAuth 2 接入方式,若非迫不得已,大多數場合不推薦使用。

密碼模式

主要是用於一些非瀏覽器的接入場景,如果要採用密碼模式,那“第三方”屬性就必須弱化,把“第三方”視作是系統中與授權服務器相對獨立的子模塊,在物理上獨立於授權服務器部署,但是在邏輯上與授權服務器仍同屬一個系統,這樣將認證和授權一併完成的密碼模式纔會有合理的應用場景:

sequenceDiagram participant User as 用戶 participant Client as 客戶端 participant Server as 授權服務器 User->>Client: 提供用戶名和密碼 Client->>Server: 請求訪問令牌(用戶名和密碼) Server->>Client: 返回訪問令牌 Client->>Server: 使用訪問令牌請求受保護的資源 Server->>Client: 提供受保護的資源

密碼模式非常簡單,就是拿着用戶名和密碼向授權服務器換令牌而已。在密碼模式下 OAuth2 不負責保障安全,只能由用戶和第三方應用來自行提供安全保障。

客戶端模式

以應用爲主體的授權模式,不涉及到用戶的登錄行爲,是客戶端模式是指第三方應用以自己的名義,向授權服務器申請許可憑證。用來訪問受保護資源,如下:

sequenceDiagram participant Client as 客戶端 participant Server as 授權服務器 Client->>Server: 提供客戶端憑據(客戶端ID和密鑰) Server->>Client: 驗證憑據並返回訪問令牌 Client->>Server: 使用訪問令牌請求受保護的資源 Server->>Client: 提供受保護的資源

客戶端模式通常是用於微服務之間的訪問,例如一些定時任務,應用之間訪問授權的操作。在微服務架構並不提倡同一個系統的各服務間有默認的信任關係,所以服務之間調用也需要先進行認證授權,然後才能通信。例如應用層常見的微服務框架 Spring Cloud 就是採用該方案保證服務間的合法調用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章