圍繞各種 permission 類和基於代碼的安全性構建的 Java SE Access Control 模型沒能緊跟 Java 平台的發展步伐,因而無法滿足當今企業系統的需求。本文將分析該問題的根源,並給出一些建議的替代方案。
Java SE 模型概述
該模型的本質是使用代碼中的 permission 類來認可執行某些操作的正確性。當某個操作要在特定的類/頁面/等中執行時,該代碼將調用 SecurityManager 的(或 AccessController 的)方法 checkPermission(Permission),向其傳遞一個由基礎 Permission 類派生的某個類的實例。然後 AccessController 將負責對整個調用堆棧執行迭代,驗證堆棧中的每個幀是否包含所需的權限。圖 1 顯示了這種過程。
圖 1. checkPermission 調用中的堆棧遍歷
需要重點了解的是,在這個模型中,permission 類將自己負責執行評估邏輯,以及定義資源結構和可用權限的任務。每個代碼框架的權限來自於對當前線程及其 CodeSource 執行 Java Principal。對於堆棧中的每個代碼框架,計算任務由已安裝的 Policy 提供程序來完成,而提供程序將負責對結果集中的每個 permission 類調用評估邏輯,以確定其中是否有潛在的 sought permission。
當代碼運行成功時,整個 checkPermission(Permission) 調用要麼返回,要麼拋出 SecurityException 表示違反了安全性規則。
Java SE 模型的歷史
Java 的安全模型可以追溯到該平台的早期時代,當時人們主要將它看作一種增強用戶體驗的浏覽器擴展機制。執行的 Java 代碼可以從各種源派生,而其中一些的來源是未知的或者不可靠的。相應地,該平台的安全性最初主要關注的是解決驗證被執行的代碼可信任的問題,而且整個游戲圍繞著在浏覽器中執行 applet。但是,這個模型只是簡單地劃分為 trusted 和 untrusted 部分,甚至連中等復雜的應用程序都無法運行。
從 1.2 版開始,Java 作為一個編程平台逐漸得到了人們的認可,而不再只是一個浏覽器擴展,Sun 開始提供更加靈活的安全功能,首先是可配置安全策略的概念。Java 文檔 介紹了它的發展情況。
當 Java 平台開始進入企業環境時,很快就會明顯地感覺到,純粹基於代碼的功能無法管理大型應用程序的安全性。Java 平台的 1.4 版引入了一種叫做 Java Authentication and Authorization Service (JAAS) 的新特性,用於將基於用戶的權限項整合到安全策略中。現在,堆棧上某個特殊代碼框架的權限既基於代碼的源(其 CodeSource),又基於驗證時分配給用戶的身份、組和角色。
當前的問題
即使經歷了如此漫長的發展,Java 的安全模型仍然最適合代碼訪問安全。但是,這個模型未能應對高層企業應用程序的需求,這需要能夠執行比簡單權限檢查更多的訪問控制功能。以下部分將討論其中的一些問題。
管理功能
通常,一般當涉及管理安全性,尤其是管理訪問控制時,唯一的最大障礙就是缺少直接而且強大的管理模型。以下列出了 Java 基於權限的模型的明顯缺陷。
權限擴散
Java 本身自帶了很多標准權限,它們由其自己的代碼調用(請參閱 Permission 及其 子類)。但是,這些權限不適合那些試圖保護自己的組件的應用程序,所以這些應用程序中相當多的一部分必須定義其自己的類。
例如,考慮一個工作流應用程序,如圖 2 所示:
圖 2. 訪問控制的用戶界面屏幕
不計算標准 SE 和 EE 權限,它們由應用程序和容器調用(但是仍然需要在該策略內管理),該頁面需要定義一些單獨的權限。首先,該應用程序需要定義一個專門的權限(稱之為 TransferPermission)來驗證該用戶被授權執行工作流的這一步流程。其次,它要檢查用戶在此屏幕上是否可以看到和使用文本框和按鈕,這需要一些其他 UI 權限(稱之為 AccountEditButtonUIPermission、AccountTextUIPermission 和 AmountTextUIPermission)。第三,此頁面中的一些數據(比如賬號)可能需要隱藏;不幸的是,Java 沒有提供任何機制來滿足該需求(請參閱 數據和功能安全的一致性 部分)。
考慮到大型應用程序(尤其是在金融和醫療行業)通常擁有數千個元素,這很快會使已定義權限的總數飙升至數百甚至數千個,使整個權限的集合難以管理。
資源模型
每個權限(無論它是標准的還是自定義的)都將其自己的資源解釋邏輯整合到評估過程。這意味著 permission 類要操作不同表現形式的應用程序資源,使安全管理員無法創建一個圖 3 所示類型的一致的應用程序管理圖。
圖 3. 資源層次結構
例如,考慮 Java 的標准 FilePermission 和 SocketPermission。前者操作資源字符串,表示文件和文件夾,使用 "/MyDir1/MyDir2/*" 格式,而後者使用主機、套接字和端口:"localhost:1024-"。通常情況下,這樣的差別使任何管理應用程序都無法解析任意 permission 類在應用程序中使用的資源字符串。
策略層次結構
在 Java 權限模型中,與資源模型密切相關的是策略層次結構的缺乏。策略層次結構意味著,根據定義良好的混合算法,策略(在應用程序資源樹的較高層定義)也可適用於較低的節點。在這裡的優勢是,安全管理員可以在較高的層次定義訪問控制策略,並且在他的命令下它們將可以適用於所有的應用程序/域。
考慮圖 3 的層次結構,根據公司的政策,在某個時間段,管理員可以限制交易人訪問交易應用程序,以及他們可以執行的操作,基於 permission 的模型不允許這種水平的控制,因為它沒有資源層次結構的概念,並且它唯一的策略組合機制存在於創建一個可應用的所有策略的 grant entries 的結合。
策略元素
訪問控制另一個重要的方面是安全策略自身的功能。Java 的策略是非常基礎的——它在 grant entries 中包含權限集,由 Principals、code origin 或 code signer(結合到 CodeSource 中)分配。
此模型缺少以下非常重要的授權策略的功能。
配置 DENY(拒絕)策略的功能
Java 將缺少明顯的 GRANT(授權)當作是隱式的 DENY(拒絕)。但是,假設缺少統一的資源模型,為了拒絕用戶或組訪問某個特定資源,安全管理員必須仔細檢查每個該策略中的授權項,以驗證它們均未授權訪問。一般情況下,這是一個開放式的任務,每次策略修改的時候都必須重做。一種更容易的替代方案是在應用程序分層結構的頂部定義一個拒絕策略,並且指定拒絕結果優先。
配置基於屬性的約束的功能
該策略自己指定某個特殊用戶(某個組的成員或具有某種角色)或代碼(某個特殊來源或簽名的代碼)對某個特殊資源擁有某種權限(例如,FilePermission("/app/user/US/MA/*", "read") 分配 read(讀取)的權限給資源 "/app/user/US/MA/*")。這表示該文件權限將必須授權給所有要求訪問該區域的基於 Massachusetts 的用戶、組或角色,並且在他們不需要它時會進行刪除(例如,如果他們搬到基於 New York 的辦公室)。對於基於 California 的雇員來說也是如此。
是基於屬性的約束(也稱為“條件”)在應用授權策略時提供真正的靈活性。這些約束允許包含外部元數據來控制何時以及如何應用策略的決策。真正的靈活性來自於從什麼地方以及如何提取該元數據:目錄(比如 LDAP)、環境(例如,操作系統),或者與實際的請求一起向應用程序提供。
回到前面的例子,應該定義一個單獨的基於屬性的策略(適用於公司內的所有用戶和地點),而不是將文件權限授權給多個用戶組:
GRANT {User, Resource, "read"} IF (User.location IN Resource.location)
將責任返回給調用者的功能
應用程序經常不足以知道訪問是否得到授權或被拒絕了,因為它需要知道與決策相關的其他信息。例如,策略可能指定一個廣泛的 GRANT,但是對某些領域進行一些約束,這需要被返回給調用應用程序(也稱為 Policy Enforcement Point 或 PEP)。這樣的返回數據叫做責任,並且提供了一個重要的擴展機制,用於擴展應用程序功能,而無需核心安全邏輯。Java 安全策略完全不提供這樣的功能。
控制
控制 (governance) 任務的目的是為進行審核回答“誰可以在什麼條件下做什麼”的問題。在通常情況下這個問題很難解決,但是 Java 的基於權限的模型使其幾乎無法進行任何大型部署。由於權限自身擁有解釋資源的任務,外部工具需要考慮任意應用程序所有可能使用的權限,以便了解哪些應用程序資源被保護了,以及如何被保護的。另外,這樣的工具將必須隨著應用程序的每個版本更新。很明顯,這使維護變成了噩夢,並且不能為此部署一個通用的審計工具。
數據和功能安全的一致性
最後但同樣重要的是,Java 的安全模型都是關於功能安全的;例如,它被設計為回答有關對特定代碼功能進行訪問的問題。但是,在很多應用程序中,同樣重要的是提供數據的一致視圖,根據用戶的權利編寫。有關在何處以及如何應用此功能的例子,請參閱 權限擴散。
一種替代方案(Java 中可使用)通常要求要麼另外開發一個機制(例如,通過利用數據庫查詢),要麼顯示整個列表,然後在用戶試圖訪問特定記錄時執行訪問控制。第一種替代方案(擁有兩個模型)完全無法管理,並且會導致不一致。第二個(只在訪問時執行檢查)暴露了太多信息,並且當向用戶顯示了記錄,但是用戶無法訪問它們的時候,會導致糟糕的可用性。
運行時功能
運行時體驗展示了另一半問題,由於下面解釋的原因,Java 的模型也存在不足。
不必要的堆棧審核
前面已經多次指出,Java 的模型是用於基於代碼的安全。相應地,每次請求 checkPermission(Permission) 時,會觸發一個 堆棧審核,以確定堆棧上的所有代碼框架是否具有所需的權限。
但是,大型與程序的安全策略不關心代碼,而關心用戶檢查。實際上,這意味著每次對檢查用戶權限的調用會變成不必要的堆棧審核。此外,只要該策略處理代碼(稱之為 Policy Decision Point,或 PDP)和應用程序處於同一過程,這就行得通。由於 AccessController 的硬編碼的評估邏輯(另請參閱 評估邏輯的可擴展性),任何外部化 PDP 的企圖都會導致對每個 checkPermission(Permission) 調用的多個遠程調用,使整個系統不可用。
評估邏輯的可擴展性
Java 的安全模型的邏輯智能以一種方式更改:通過替換 安全策略提供程序。這種限制使得無法自定義堆棧評估算法,在 AccessController 中硬編碼,使遠程調用更高效,或者優化基於用戶的訪問控制檢查。
批量授權
當需要對單個組件的多個元素進行授權時,通過將多個請求包裝到一個對 PDP 的調用中,批量授權確實可以幫助提高應用程序的性能。例如,查看 管理功能 部分中的網頁圖片。該頁面必須進行多個請求,以驗證頁面中所有要求的元素。但是,如果把他們打包在一起,只進行一個調用,那效率就會高得多。不幸的是,Java 對此也不提供任何支持。
與批量授權類似的還有 querying access(查詢訪問)的問題,這表示應用程序詢問“使用給定的操作和屬性,在某個資源節點下某個特殊用戶可以訪問哪些資源?”根據返回的結果,應用程序隨後查看子資源,對其進行特殊操作。例如,網頁可能在加載時查詢用戶可以訪問哪些控制,然後隱藏/禁用那些策略不允許的控制。Java 由於缺乏單個資源模型,無法確定可用的權限,因此沒有這樣的功能。
返回結果
如在 策略元素 中曾經解釋過的一樣,Java 缺乏責任的概念來向 PEP 客戶端傳回有關策略決策的其他信息。這使它無法使用基於 JSE 的策略來控制數據安全性,例如,數據和供安全的一致性 解釋了這個問題。
替代方案
如前所述,Java SE 平台安全從未真正從其基於浏覽器的基礎中成長起來。與權限模型相關的管理和運行時缺陷使其不適合用於企業級應用程序。安全策略和授權是 Java 明顯不足的領域。但是,這並沒有妨礙其在企業中的應用——相反,大部分大型公司要麼內部開發用於授權的解決方案,要麼購買第三方的軟件。以下部分(雖然不打算涉及 entitlements 市場的形勢)將簡要介紹可以替代 Java 架構的方法。
XACML 標准
標准的行業術語已經被整合到 eXtensible Access Control Markup Language (XACML) 標准 中。盡管該標准自身並未提供安全性保護,但其概念是 Fine-Grained Entitlements (FGE) 行業的多個產品的基礎,幫助定義了通用的術語和組件。其中很多概念(在以上部門有解釋)在 XACML 標准中也有反映。
XACML 自身並未定義 Java 綁定——只有一個 SOAP 配置文件允許發送運行時授權請求。Sun 公司的 XACML 愛好者們早期曾經計劃定義一個 J2SE Policy Profile(例如,一個 Java Policy Provider for XACML 策略),但是鑒於過去五年中它沒有任何進展,我認為它的有點不值得認真考慮。
相反,不同的提供商已經圍繞 XACML 標准開發產品,使其功能可通過其自己的 API 被 Java 應用程序使用,這通常會帶來比純粹的標准簡化(而且更容易管理!)的模型。就像其他領域一樣,隨著時間的流逝,我們將看到市場上現有的各種不同類型的 Java 授權 API 會匯聚成一種行業標准的 Java API。
JSR 115
JSR 115 標准有著漫長而且悲涼的歷史,因為盡管供應商在 2003 年就認可了它,但是他們並沒有急於使用 JSR 中指定的解決方案來代替他們的特定於平台的解決方案。而且大客戶也不急於利用它——實際上,由於 JACC 提供商在接受該標准後不久就加入了 Weblogic,至今還沒有一個對此功能的請求或查詢。
引起這種冷淡的原因在於該標准本身的目的,以及它提供給公眾的工具。JSR 115 專用於修復 EE-to-SE 綁定在授權領域中的一個漏洞;在該領域中,EE 和 SE 機制只是共同存在,但是彼此並未交互。盡管解決了將特定於 EE 的權限帶入 SE 域的最初需求,它仍然依靠 SE 權限模型,因此具有以上討論的所有缺陷。另外,它沒有采取任何措施來應對細粒度權利的需求(在上一部分列出)——而這正是公司真正需要的。盡管一些人會辯解說,其機制(比如 ContextHandlers)足夠靈活,可以支持 FGE 需求,但是這樣一個解決方案的技術和功能有點仍然值得懷疑。這些(以及其他)有關 SE/EE 安全整合的問題在我以前有關此主題的文章中也有涉及,“在 Java EE 和 SOA 環境中使用 JAAS”。
結束語
如上所述,Java 自身的權限模型非常不適合用於大型企業系統。本文並不打算全面地剖析此問題,而只是簡要介紹了一些相關的問題。有關細粒度權限的更多信息,建議熟悉 XACML 標准的概念,因為它定義了各方都接受的組件和術語。盡管現在已經有了開源的 XACML 實現和工具包,並不建議立即深入使用它們,因為它們僅是實現了標准,留下了很多以上列出的需要考慮的重要問題。相反,作者強烈建議考慮使用現在市場上已有的第三方細粒度權限解決方案。