易文康,程 驊,程耕國
(武漢科技大學(xué) 信息科學(xué)與工程學(xué)院,武漢 430081)
隨著互聯(lián)網(wǎng)技術(shù)的飛速發(fā)展,學(xué)校、企業(yè)和政府機(jī)關(guān)等機(jī)構(gòu)所建立的信息管理系統(tǒng),其管理的對象日益復(fù)雜,用戶所能訪問的數(shù)據(jù)資源日趨龐大,導(dǎo)致用戶的權(quán)限管理和日常維護(hù)變得越來越繁瑣。為在實(shí)現(xiàn)資源信息共享的同時(shí),避免出現(xiàn)授權(quán)用戶越權(quán)訪問未授權(quán)的系統(tǒng)資源以及未授權(quán)用戶非法訪問系統(tǒng)資源等一系列Web[1]系統(tǒng)中的信息安全問題[2],需要進(jìn)一步深入研究基于角色的訪問控制技術(shù)[3-4]和Shiro的授權(quán)機(jī)制,確保用戶對系統(tǒng)數(shù)據(jù)資源的訪問都是經(jīng)過授權(quán)的,并防止非法用戶的訪問。這對保證系統(tǒng)中數(shù)據(jù)資源的安全性、保密性和完整性是非常必要的。本文主要研究如何運(yùn)用Shiro安全框架[5]來阻止Web系統(tǒng)中的越權(quán)訪問控制(Broken Access Control,BAC)。
越權(quán)訪問[6]即跨越權(quán)限訪問,是Web應(yīng)用中一種常見的安全缺陷,其產(chǎn)生原因是沒有對訪問用戶提交的數(shù)據(jù)參數(shù)進(jìn)行權(quán)限檢查或者缺少跨域訪問的限制。越權(quán)訪問可以直接繞過基礎(chǔ)的網(wǎng)絡(luò)安全服務(wù)防御,它通過前端對數(shù)據(jù)或者參數(shù)進(jìn)行請求構(gòu)造、遍歷,在取得完整的Web應(yīng)用程序或者數(shù)據(jù)庫權(quán)限前就可以得到相關(guān)用戶的個(gè)人信息。若沒有對越權(quán)訪問采取有效的解決或防御措施,將極有可能泄露客戶個(gè)人、企業(yè)和政府等機(jī)構(gòu)的重要信息,存在很大的網(wǎng)絡(luò)信息安全隱患,造成不可估量的經(jīng)濟(jì)損失。
針對Web系統(tǒng)訪問控制中越權(quán)訪問的安全隱患,研究者提出了各種解決方案,其中Shiro框架逐漸受到人們的重視。本文通過使用Shiro框架的授權(quán)方式和一種權(quán)限訪問控制算法來解決此安全問題。
Shiro是Apache 系列的一個(gè)Java開源安全開發(fā)框架[7],其本身具有良好的健壯性和易用性。Shiro提供了認(rèn)證、授權(quán)、會(huì)話管理、加密等功能,可以為學(xué)校、企業(yè)和政府機(jī)關(guān)等機(jī)構(gòu)提供信息安全解決方案。同時(shí),Shiro本身集成了許多保護(hù)Java應(yīng)用的特性,如支持Web應(yīng)用[8]、線程和并發(fā)、緩存機(jī)制和測試工具等。與其他安全框架相比,Shiro的安全認(rèn)證和授權(quán)方式比較簡潔且容易操作,編寫的代碼量顯著減少。本文主要研究如何將Shiro的授權(quán)功能應(yīng)用在Web網(wǎng)絡(luò)安全問題中,解決越權(quán)訪問的問題。
當(dāng)用戶登錄系統(tǒng)并進(jìn)行數(shù)據(jù)操作訪問Web數(shù)據(jù)庫[9]時(shí),可能由于本身系統(tǒng)設(shè)計(jì)上存在的安全漏洞[10]或者用戶權(quán)限分配和管理上存在的缺陷,導(dǎo)致授權(quán)用戶越權(quán)訪問未授權(quán)的數(shù)據(jù)庫資源或者未授權(quán)用戶非法訪問數(shù)據(jù)庫資源。為了阻止這些安全隱患的發(fā)生,可在Web應(yīng)用中集成Shiro安全框架。
首先在Web工程里的web.xml配置文件中定義Shiro Servlet過濾器實(shí)現(xiàn)Web應(yīng)用和Shiro框架的集成。該過濾器會(huì)過濾用戶發(fā)送來的所有請求,根據(jù)實(shí)際需求執(zhí)行特定的邏輯判斷,當(dāng)請求滿足一定要求時(shí)才允許通過,從而保證用戶在進(jìn)行Web數(shù)據(jù)庫訪問時(shí),會(huì)先對用戶的訪問權(quán)限進(jìn)行判斷,若擁有相應(yīng)的權(quán)限,則執(zhí)行對應(yīng)的業(yè)務(wù)邏輯操作,訪問數(shù)據(jù)庫并得到數(shù)據(jù)。數(shù)據(jù)庫訪問流程如圖1所示。
圖1 基于Shiro的數(shù)據(jù)庫訪問流程
授權(quán),其實(shí)質(zhì)就是訪問控制[11-12],控制系統(tǒng)用戶能夠訪問Web系統(tǒng)中的哪些資源,能執(zhí)行哪些操作(如訪問頁面、編輯數(shù)據(jù)等),這些一般是通過系統(tǒng)管理員分配給用戶的角色和權(quán)限來決定的。Shiro支持權(quán)限的概念,能友好地與Web系統(tǒng)集成[13],還能夠通過運(yùn)用通配符靈活地對權(quán)限進(jìn)行匹配和檢查。目前,Shiro提供以下3種方式的授權(quán):
1)編程式授權(quán)。通過編寫if/else等一系列Java的授權(quán)代碼塊來實(shí)現(xiàn)當(dāng)前用戶的授權(quán)操作,例如:
Subject currentUser=SecurityUtils.getSubject();
if (currentUser.hasRole("read")) {
//有角色"read"權(quán)限
} else {
//沒有角色"read"權(quán)限}
2)JSP/GSP標(biāo)簽式授權(quán)。在JSP/GSP頁面通過相應(yīng)的標(biāo)簽來實(shí)現(xiàn)授權(quán),例如:
3)注解式授權(quán)。在執(zhí)行的Java方法上放置相應(yīng)的注解來實(shí)現(xiàn)系統(tǒng)用戶的授權(quán)操作(前提是開發(fā)的系統(tǒng)需要支持面向切面的編程[14]),例如:
@RequiresPermissions("user:read")
//擁有角色"user"中的"read"權(quán)限
void someMethod();
以前訪問數(shù)據(jù)資源時(shí),需要先對訪問的url進(jìn)行權(quán)限配置,在spring-shiro.xml中編寫大量的Java代碼,與最開始的Hibernate框架相同,需要編寫大量的hbm配置文件,顯得十分冗余,不利于后期維護(hù),因而迫使開發(fā)者們逐漸選擇注解形式來進(jìn)行程序的開發(fā)和授權(quán)等操作。本文采用基于Shiro框架的JSP/GSP標(biāo)簽式授權(quán)和注解式授權(quán)來實(shí)現(xiàn)權(quán)限操作,配置非常簡潔方便,顯著減少了授權(quán)部分的代碼量,同時(shí)還方便后期的管理和維護(hù)。
以如下Shiro權(quán)限注解為例進(jìn)行分析:
@RequiresPermissions("user:find","user:add",…)
void someMethod();
注解@RequiresPermissions要求當(dāng)前登錄用戶必須同時(shí)具備user:find和user:add權(quán)限時(shí),才能繼續(xù)執(zhí)行注解下面的方法someMethod(),否則系統(tǒng)將會(huì)拋出異常AuthorizationException。
在Web系統(tǒng)中,該注解作用于Java方法上,表示對訪問這些方法的用戶進(jìn)行訪問攔截,當(dāng)判斷出用戶擁有該權(quán)限時(shí)才能繼續(xù)訪問,使得在用戶訪問后臺(tái)數(shù)據(jù)資源時(shí),先判定其是否擁有訪問此方法的權(quán)限,對阻止越權(quán)訪問有一定的作用。但在設(shè)計(jì)Web系統(tǒng)時(shí),可能在開發(fā)授權(quán)這塊考慮不周或者部分業(yè)務(wù)邏輯代碼遺漏權(quán)限判斷功能,導(dǎo)致用戶在未授權(quán)的情況下越權(quán)訪問這部分?jǐn)?shù)據(jù),甚至非法訪客通過一些黑客技術(shù)來非法訪問系統(tǒng)數(shù)據(jù)庫中的數(shù)據(jù)資源,泄露了客戶的個(gè)人隱私和一些敏感重要的商業(yè)信息,造成無法挽回的損失。
為避免越權(quán)訪問情況的發(fā)生,本文在Shiro提供的權(quán)限注解@RequiresPermissions基礎(chǔ)上,提出一個(gè)作用于Java類上的改進(jìn)的權(quán)限注解。
2.3.1 實(shí)現(xiàn)方法
根據(jù)Shiro提供的作用于Java方法的權(quán)限注解@RequiresPermissions,本文提出一個(gè)改進(jìn)的作用于Java類的權(quán)限注解@ClassPermissions,實(shí)現(xiàn)步驟為:
1)編寫一個(gè)ClassPermissions.java,在其中定義基于類的權(quán)限注解@ClassPermissions:
public @interface ClassPermissions {
String[] value();}
2)在Spring[15]配置文件spring-shiro.xml中添加:
3)繼承AuthorizingAnnotationHandler類,將構(gòu)造函數(shù)中new的對象替換成自己的AOP實(shí)現(xiàn)。關(guān)鍵代碼如下:
public ClassAuthorizingAnnotationHandler() {
super(ClassPermissions.class);}
@Override
public void assertAuthorized(Annotation an) throws AuthorizationException {
ClassPermissions per = (ClassPermissions) an;
String[] permissions =per.value();
getSubject().checkPermissions(permissions);
return;} }
4)把spring所提供的AopAllianceAnnotations-AuthorizingMethodInterceptor重寫一個(gè)自己的,修改權(quán)限驗(yàn)證攔截器棧的設(shè)置,修改權(quán)限驗(yàn)證的攔截器。關(guān)鍵代碼如下:
interceptors.add(new ClassAnnotationMethod
Interceptor(resolver));
//自定義
interceptors.add(new ClassAuthorizingAnnotation
MethodInterceptor());
5)實(shí)現(xiàn)所需的權(quán)限驗(yàn)證攔截器,根據(jù)spring所提供的類AuthorizingAnnotationMethodInterceptorl重寫一個(gè)自己的類。關(guān)鍵代碼如下:
publicClassAuthorizingAnnotationMethod-
Interceptor (){
super(new ClassAuthorizingAnnotationHandler());}
public ClassAuthorizingAnnotationMethod-
Interceptor(AnnotationResolver resolver){
super(new ClassAuthorizingAnnotationHandler(),
resolver);}
6)實(shí)現(xiàn)所需的權(quán)限處理器,繼承spring提供的類AuthorizationAttributeSourceAdvisor,實(shí)現(xiàn)作用于類的權(quán)限注解,獲取類上的注解以及類下所有方法的注解。關(guān)鍵代碼如下:
private static final Class extends Annotation>[ ] annotationClass=new Class[] {//注解權(quán)限
ClassPermissions.class,
RequiresPermissions.class,……};
//匹配帶有注解的方法
@Override
public boolean matches(Method m,Classc){
boolean flag = super.matches(m,c);
//若方法上沒有權(quán)限注解,則獲取類上權(quán)限注解
if(!flag && isAuthzAnnotationPresent(c) && isWebAnnota tionPresent(m)){
flag = true;}
return flag;}
//查看方法上是否有權(quán)限注解
private boolean isAuthzAnnotationPresent(Method m) { for(Class extends Annotation> ann:
annotationClass {
Annotation a=AnnotationUtils.findAnnotation(m,ann);
if (a !=null) {return true;}
}return false;}
2.3.2 類與方法之間的安全作用機(jī)制
對集成了Shiro框架的Web系統(tǒng)各個(gè)功能模塊的url訪問都需要角色權(quán)限。當(dāng)用戶訪問時(shí),后臺(tái)會(huì)根據(jù)前端的 url地址定位到所對應(yīng)方法上的權(quán)限注解。spring會(huì)先掃描Shiro注解類的matches方法(上述第6個(gè)步驟),并通過返回true/false的方式來判斷某個(gè)方法是否帶有Shiro權(quán)限注解。若返回true,則再判斷當(dāng)前訪問操作是否滿足方法上的注解權(quán)限;若返回false,表示方法上沒有權(quán)限注解,則會(huì)去獲取所屬類上的權(quán)限注解@ClassPermissions,再判斷當(dāng)前訪問操作的權(quán)限。
結(jié)合上節(jié)中實(shí)現(xiàn)的作用于Java類上的權(quán)限注解@ClassPermissions,本文提出一個(gè)基于Shiro標(biāo)簽式授權(quán)和注解式授權(quán)的權(quán)限訪問控制算法。
算法基于Shiro標(biāo)簽和注解的訪問控制算法
輸入訪問者的請求request=
輸出訪問者得到的視圖
步驟1根據(jù)請求request獲得user對應(yīng)的角色信息,并通過角色I(xiàn)D (useId)獲取Shiro中保存的對應(yīng)的權(quán)限信息。
步驟2Shiro過濾器攔截所有的請求,對每個(gè)請求進(jìn)行權(quán)限判斷。
步驟3若是前端請求,判斷相應(yīng)方法上的JSP/GSP權(quán)限標(biāo)簽。若擁有該權(quán)限,則跳轉(zhuǎn)至步驟7,否則跳轉(zhuǎn)至步驟8。
步驟4若是后臺(tái)請求,判斷相關(guān)Java方法上是否有權(quán)限注解。若有,則跳轉(zhuǎn)至步驟5,否則跳轉(zhuǎn)至步驟6。
步驟5若擁有權(quán)限注解@RequiresPermissions中標(biāo)識(shí)的權(quán)限,則跳轉(zhuǎn)至步驟7,否則跳轉(zhuǎn)至步驟8。
步驟6獲取并判斷所屬Java類上的權(quán)限注解@ClassPermissions,若擁有注解中標(biāo)識(shí)的權(quán)限,則跳轉(zhuǎn)至步驟7,否則跳轉(zhuǎn)至步驟8。
步驟7允許訪問,獲取數(shù)據(jù)庫中的匹配信息,然后跳轉(zhuǎn)至步驟9。
步驟8禁止訪問,拋出異常。
步驟9輸出經(jīng)過權(quán)限過濾后的數(shù)據(jù)信息。
用戶登錄時(shí)發(fā)出請求request=
基于 Shiro的授權(quán)流程如下:首先系統(tǒng)會(huì)調(diào)用Subject.isPermitted("權(quán)限串")方法,然后委托給securityManager進(jìn)行處理,通過securityManager內(nèi)部的Authorizer(默認(rèn)是實(shí)現(xiàn)ModularRealmAuthorizer)來進(jìn)行真正的授權(quán)處理。ModularRealmAuthorizer會(huì)調(diào)用Realm的授權(quán)方法doGetAuthorizationInfo,從數(shù)據(jù)庫查詢權(quán)限數(shù)據(jù),返回ModularRealmAuthorizer,ModularRealmAuthorizer會(huì)調(diào)用PermissionResolver進(jìn)行權(quán)限串比對。若從Realm中獲取的當(dāng)前Subject的角色信息(即權(quán)限串)與傳入的isPermitted("權(quán)限串")相匹配,則返回ture,有訪問權(quán)限;否則返回false,沒有訪問權(quán)限。
對于后臺(tái)的訪問請求,系統(tǒng)會(huì)先定位到具體的Java方法上,并查找方法上的權(quán)限注解,例如@RequiresPermissions("user:find")。若方法上存在權(quán)限注解,則按照上述Shiro的授權(quán)流程來進(jìn)行授權(quán)處理;若方法上沒有權(quán)限注解,則會(huì)去獲取所在類上的權(quán)限注解@ClassPermissions("user:find")再進(jìn)行授權(quán)處理,從而實(shí)現(xiàn)對后臺(tái)訪問請求的權(quán)限控制。
綜上所述,基于權(quán)限訪問控制的用戶授權(quán)流程如圖2所示。
圖2 基于權(quán)限訪問控制的用戶授權(quán)流程
3.3.1 安全性
一個(gè)集成了Shiro的Web系統(tǒng),基本每個(gè)功能模塊的Controller層都有同樣的add、list等方法,這些方法不處理業(yè)務(wù)邏輯,只是把請求從Controller層轉(zhuǎn)到Service層處理完后,將結(jié)果再轉(zhuǎn)給相應(yīng)的視圖。此時(shí)只需在Controller層編寫抽象類,并實(shí)現(xiàn)這些方法,再使用@RequestMapping標(biāo)記此類。其他需要實(shí)現(xiàn)增刪改查功能的Controller都繼承此抽象類,每個(gè)類只需編寫自己的視圖地址即可。此時(shí),需要注解@RequiresPermissions能實(shí)現(xiàn)對類的注解。但是,根據(jù)文獻(xiàn)[5]和Apache Shiro官網(wǎng)可知,該注解只能作用在方法上,導(dǎo)致其無法在Controller層的抽象類上實(shí)現(xiàn)權(quán)限驗(yàn)證功能。
本文提出的權(quán)限注解@ClassPermissions可針對某個(gè)類進(jìn)行注解,解決了上述技術(shù)難題。其相應(yīng)的權(quán)限訪問控制算法與Apache Shiro官網(wǎng)提供的@RequiresPermissions的權(quán)限驗(yàn)證過程相比,相當(dāng)于增加了3.1節(jié)中步驟4和步驟6的判斷,避免了開發(fā)過程中某些方法上缺少權(quán)限驗(yàn)證的問題,提高了系統(tǒng)的安全性。同時(shí),不再需要對Controller層的所有方法進(jìn)行注解,節(jié)省了大量的開發(fā)時(shí)間,使得授權(quán)更加靈活,便于管理和維護(hù)。
3.3.2 有效性
本文采用某公司的角色信息管理系統(tǒng)進(jìn)行效果測試。該系統(tǒng)類似于文獻(xiàn)[13]中的系統(tǒng),其采用了B/S體系結(jié)構(gòu),以Spring+SpringMVC+Hibernate開源框架設(shè)計(jì)開發(fā),整合了Shiro框架,使用MySQL數(shù)據(jù)庫。
創(chuàng)建一個(gè)沒有任何操作權(quán)限的普通用戶,測試100個(gè)url:http://localhost:8080/spring/rest/…①…/…②…,其中:①表示系統(tǒng)的功能模塊名;②表示每個(gè)模塊中增刪改查等方法的方法名,觀察并記錄網(wǎng)頁的輸出結(jié)果;然后在各個(gè)功能模塊的Controller層上實(shí)現(xiàn)權(quán)限注解@ClassPermissions,再觀察輸出結(jié)果。實(shí)驗(yàn)結(jié)果對比如表1所示。由對比數(shù)據(jù)可知,實(shí)現(xiàn)類上的注解后可有效地阻止越權(quán)訪問。
表1 訪問結(jié)果對比
3.3.3 普適性
只要開發(fā)的Web應(yīng)用系統(tǒng)支持面向切面的編程,即可很容易地實(shí)現(xiàn)作用于Java類的權(quán)限注解@ClassPermissions,從而實(shí)現(xiàn)方法和類上的權(quán)限控制。因此,本文算法在實(shí)際開發(fā)應(yīng)用中具有很好的普遍適用性。
針對Web系統(tǒng)中的越權(quán)訪問問題,本文提出一種基于Shiro安全框架的權(quán)限訪問控制算法,通過協(xié)同使用作用于Java方法和類上的權(quán)限注解,實(shí)現(xiàn)整個(gè)Web系統(tǒng)后臺(tái)的授權(quán)。與之前僅采用Shiro本身提供的權(quán)限注解來實(shí)現(xiàn)系統(tǒng)授權(quán)相比,該算法可有效阻止越權(quán)訪問的發(fā)生,提高系統(tǒng)的安全性,同時(shí)降低開發(fā)者工作的復(fù)雜度,使操作更加靈活方便。下一步工作將繼續(xù)運(yùn)用Shiro和其他安全框架來解決Web系統(tǒng)中存在的各種安全問題。