1. 程式人生 > 其它 >許可權系統設計模型分析(DAC,MAC,RBAC,ABAC)

許可權系統設計模型分析(DAC,MAC,RBAC,ABAC)

此篇文章主要嘗試將世面上現有的一些許可權系統設計做一下簡單的總結分析,個人水平有限,如有錯誤請不吝指出。

術語

這裡對後面會用到的詞彙做一個說明,老司機請直接翻到常見設計模式

使用者

發起操作的主體。

物件(Subject)

指操作所針對的客體物件,比如訂單資料或圖片檔案。

許可權控制表 (ACL: Access Control List)

用來描述許可權規則或使用者和許可權之間關係的資料表。

許可權 (Permission)

用來指代對某種物件的某一種操作,例如“新增文章的操作”。

許可權標識

許可權的代號,例如用“ARTICLE_ADD”來指代“新增文章的操作”許可權。

常見設計模式

自主訪問控制(DAC: Discretionary Access Control)

系統會識別使用者,然後根據被操作物件(Subject)的許可權控制列表(ACL: Access Control List)或者許可權控制矩陣(ACL: Access Control Matrix)的資訊來決定使用者的是否能對其進行哪些操作,例如讀取或修改。

而擁有物件許可權的使用者,又可以將該物件的許可權分配給其他使用者,所以稱之為“自主(Discretionary)”控制。

這種設計最常見的應用就是檔案系統的許可權設計,如微軟的NTFS。

DAC最大缺陷就是對許可權控制比較分散,不便於管理,比如無法簡單地將一組檔案設定統一的許可權開放給指定的一群使用者。

Windows的檔案許可權

強制訪問控制(MAC: Mandatory Access Control)

MAC是為了彌補DAC許可權控制過於分散的問題而誕生的。在MAC的設計中,每一個物件都都有一些許可權標識,每個使用者同樣也會有一些許可權標識,而使用者能否對該物件進行操作取決於雙方的許可權標識的關係,這個限制判斷通常是由系統硬性限制的。比如在影視作品中我們經常能看到特工在查詢機密檔案時,螢幕提示需要“無法訪問,需要一級安全許可”,這個例子中,檔案上就有“一級安全許可”的許可權標識,而使用者並不具有。

MAC非常適合機密機構或者其他等級觀念強烈的行業,但對於類似商業服務系統,則因為不夠靈活而不能適用。

RedHat MLS

Red Hat: MLS

基於角色的訪問控制(RBAC: Role-Based Access Control)

因為DAC和MAC的諸多限制,於是誕生了RBAC,並且成為了迄今為止最為普及的許可權設計模型。

RBAC在使用者和許可權之間引入了“角色(Role)”的概念(暫時忽略Session這個概念):

RBAC核心設計

圖片來自Apache Directory

如圖所示,每個使用者關聯一個或多個角色,每個角色關聯一個或多個許可權,從而可以實現了非常靈活的許可權管理。角色可以根據實際業務需求靈活建立,這樣就省去了每新增一個使用者就要關聯一遍所有許可權的麻煩。簡單來說RBAC就是:使用者關聯角色,角色關聯許可權。另外,RBAC是可以模擬出DAC和MAC的效果的。

例如資料庫軟體MongoDB便是採用RBAC模型,對資料庫的操作都劃分成了許可權(MongoDB許可權文件):

許可權標識說明
find 具有此許可權的使用者可以執行所有和查詢有關的命令,如:aggregate、checkShardingIndex、count等。
insert 具有此許可權的使用者可以執行所有和新建資料有關的命令:insert和create等。
collStats 具有此許可權的使用者可以對指定database或collection執行collStats命令。
viewRole 具有此許可權的使用者可以檢視指定database的角色資訊。

基於這些許可權,MongoDB提供了一些預定義的角色(MongoDB預定義角色文件,使用者也可以自己定義角色):

角色findinsertcollStatsviewRole
read
readWrite
dbAdmin
userAdmin

最後授予使用者不同的角色,就可以實現不同粒度的許可權分配了。

目前市面上絕大部分系統在設計許可權系統時都採用RBAC模型。然而也有的系統錯誤地實現了RBAC,他們採用的是判斷使用者是否具有某個角色而不是判斷許可權,例如以下程式碼:

<?php

if ($user->hasRole('hr')) {
    // 執行某種只有“HR”角色才能做的功能,例如給員工漲薪…
    // ...
}

如果後期公司規定部門經理也可以給員工漲薪,這時就不得不修改程式碼了。

以上基本就是RBAC的核心設計(RBAC Core)。而基於核心概念之上,RBAC規範還提供了擴充套件模式。

角色繼承(Hierarchical Role)

RBAC 1

帶有角色繼承的RBAC。圖片來自Apache Directory

顧名思義,角色繼承就是指角色可以繼承於其他角色,在擁有其他角色許可權的同時,自己還可以關聯額外的許可權。這種設計可以給角色分組和分層,一定程度簡化了許可權管理工作。

職責分離(Separation of Duty)

為了避免使用者擁有過多許可權而產生利益衝突,例如一個籃球運動員同時擁有裁判的許可權(看一眼就給你判犯規狠不狠?),另一種職責分離擴充套件版的RBAC被提出。

職責分離有兩種模式:

  • 靜態職責分離(Static Separation of Duty):使用者無法同時被賦予有衝突的角色。
  • 動態職責分離(Dynamic Separation of Duty):使用者在一次會話(Session)中不能同時啟用自身所擁有的、互相有衝突的角色,只能選擇其一。
RBAC 2

靜態職責分離。圖片來自Apache Directory

RBAC 3

動態職責分離。圖片來自Apache Directory

講了這麼多RBAC,都還只是在使用者和許可權之間進行設計,並沒有涉及到使用者和物件之間的許可權判斷,而在實際業務系統中限制使用者能夠使用的物件是很常見的需求。例如華中區域的銷售沒有許可權查詢華南區域的客戶資料,雖然他們都具有銷售的角色,而銷售的角色擁有查詢客戶資訊的許可權。

那麼我們應該怎麼辦呢?

使用者和物件的許可權控制

在RBAC標準中並沒有涉及到這個內容(RBAC基本只能做到對一類物件的控制),但是這裡講幾種基於RBAC的實現方式。

首先我們看看PHP框架Yii 1.X的解決方案(2.X中程式碼更為優雅,但1.X的示例程式碼更容易看明白):

<?php

$auth=Yii::app()->authManager;
 
$auth->createOperation('createPost','create a post');
$auth->createOperation('readPost','read a post');
$auth->createOperation('updatePost','update a post');
$auth->createOperation('deletePost','delete a post');

// 主要看這裡。
// 這裡建立了一個名為`updateOwnPost`的許可權,並且寫了一段程式碼用來檢驗使用者是否為該帖子的作者
$bizRule='return Yii::app()->user->id==$params["post"]->authID;';
$task=$auth->createTask('updateOwnPost','update a post by author himself',$bizRule);
$task->addChild('updatePost');
 
$role=$auth->createRole('reader');
$role->addChild('readPost');
 
$role=$auth->createRole('author');
$role->addChild('reader');
$role->addChild('createPost');
$role->addChild('updateOwnPost');
 
$role=$auth->createRole('editor');
$role->addChild('reader');
$role->addChild('updatePost');
 
$role=$auth->createRole('admin');
$role->addChild('editor');
$role->addChild('author');
$role->addChild('deletePost');

實現效果:

Yii 1.X許可權圖

圖片來自Yii官方WiKi

在這個Yii的官方例子中,updateOwnPost在判斷使用者是否具有updatePost許可權的基礎上更進一步判斷了使用者是否有許可權操作這個特定的物件,並且這個判斷邏輯是通過程式碼設定的,非常靈活。

不過大部分時候我們並不需要這樣的靈活程度,會帶來額外的開發和維護成本,而另一種基於模式匹配規則的物件許可權控制可能更適合。例如判斷使用者是否對Id為123的文章具有編輯的許可權,程式碼可能是這樣的:

<?php

// 假設articleId是動態獲取的
$articleId = 123;

if ($user->can("article:edit:{$articleId}")) {
    // ...
}

而給使用者授權則有多種方式可以選擇:

<?php

// 允許使用者編輯Id為123的文章
$user->grant('article:edit:123');

// 使用萬用字元,允許使用者編輯所有文章
$user->grant('article:edit:*');

雖然不及Yii方案的靈活,但某些場景下這樣就夠用了。

如果大家還有更好的方案,歡迎在評論中提出。

基於屬性的許可權驗證(ABAC: Attribute-Based Access Control)

ABAC被一些人稱為是許可權系統設計的未來。

不同於常見的將使用者通過某種方式關聯到許可權的方式,ABAC則是通過動態計算一個或一組屬性來是否滿足某種條件來進行授權判斷(可以編寫簡單的邏輯)。屬性通常來說分為四類:使用者屬性(如使用者年齡),環境屬性(如當前時間),操作屬性(如讀取)和物件屬性(如一篇文章,又稱資源屬性),所以理論上能夠實現非常靈活的許可權控制,幾乎能滿足所有型別的需求。

例如規則:“允許所有班主任在上課時間自由進出校門”這條規則,其中,“班主任”是使用者的角色屬性,“上課時間”是環境屬性,“進出”是操作屬性,而“校門”就是物件屬性了。為了實現便捷的規則設定和規則判斷執行,ABAC通常有配置檔案(XML、YAML等)或DSL配合規則解析引擎使用。XACML(eXtensible Access Control Markup Language)是ABAC的一個實現,但是該設計過於複雜,我還沒有完全理解,故不做介紹。

總結一下,ABAC有如下特點:

  1. 集中化管理
  2. 可以按需實現不同顆粒度的許可權控制
  3. 不需要預定義判斷邏輯,減輕了許可權系統的維護成本,特別是在需求經常變化的系統中
  4. 定義許可權時,不能直觀看出使用者和物件間的關係
  5. 規則如果稍微複雜一點,或者設計混亂,會給管理者維護和追查帶來麻煩
  6. 許可權判斷需要實時執行,規則過多會導致效能問題

既然ABAC這麼好,那最流行的為什麼還是RBAC呢?

我認為主要還是因為大部分系統對許可權控制並沒有過多的需求,而且ABAC的管理相對來說太複雜了。Kubernetes便因為ABAC太難用,在1.8版本里引入了RBAC的方案

ABAC有時也被稱為PBAC(Policy-Based Access Control)或CBAC(Claims-Based Access Control)。

結語

許可權系統設計可謂博大精深,這篇文章只是介紹了一點皮毛。

隨著人類在資訊化道路上越走越遠,許可權系統的設計也在不斷創新,但目前好像處在了平臺期。

可能因為在RBAC到ABAC之間有著巨大的鴻溝,無法輕易跨越,也可能是一些基於RBAC的微創新方案還不夠規範化從而做到普及。不過在服務化架構的浪潮下,未來這一塊必然有極高的需求,也許巨頭們已經開始佈局了。

參考文件

NTFS檔案系統許可權

Solaris許可權模型

百度百科:訪問控制

Red Hat: Multi-Level Security (MLS)

冰雲:An Introduction To Role-Based Access Control

NIST: Role-Based Access Control

MongoDB RBAC

Stackoverflow: Group vs role Any real difference?

Yii: Getting to Understand Hierarchical RBAC Scheme

Role-Based Access Control in Computer Security

(Syracuse University: Role-Based Access Control (RBAC)

Yii 2.0 Guide

WIKIPEDIA: Computer access control

https://www.jianshu.com/p/ce0944b4a903(出處)