易文康,程 骅,程耕国
(武汉科技大学 信息科学与工程学院,武汉 430081)
随着互联网技术的飞速发展,学校、企业和政府机关等机构所建立的信息管理系统,其管理的对象日益复杂,用户所能访问的数据资源日趋庞大,导致用户的权限管理和日常维护变得越来越繁琐。为在实现资源信息共享的同时,避免出现授权用户越权访问未授权的系统资源以及未授权用户非法访问系统资源等一系列Web[1]系统中的信息安全问题[2],需要进一步深入研究基于角色的访问控制技术[3-4]和Shiro的授权机制,确保用户对系统数据资源的访问都是经过授权的,并防止非法用户的访问。这对保证系统中数据资源的安全性、保密性和完整性是非常必要的。本文主要研究如何运用Shiro安全框架[5]来阻止Web系统中的越权访问控制(Broken Access Control,BAC)。
越权访问[6]即跨越权限访问,是Web应用中一种常见的安全缺陷,其产生原因是没有对访问用户提交的数据参数进行权限检查或者缺少跨域访问的限制。越权访问可以直接绕过基础的网络安全服务防御,它通过前端对数据或者参数进行请求构造、遍历,在取得完整的Web应用程序或者数据库权限前就可以得到相关用户的个人信息。若没有对越权访问采取有效的解决或防御措施,将极有可能泄露客户个人、企业和政府等机构的重要信息,存在很大的网络信息安全隐患,造成不可估量的经济损失。
针对Web系统访问控制中越权访问的安全隐患,研究者提出了各种解决方案,其中Shiro框架逐渐受到人们的重视。本文通过使用Shiro框架的授权方式和一种权限访问控制算法来解决此安全问题。
Shiro是Apache 系列的一个Java开源安全开发框架[7],其本身具有良好的健壮性和易用性。Shiro提供了认证、授权、会话管理、加密等功能,可以为学校、企业和政府机关等机构提供信息安全解决方案。同时,Shiro本身集成了许多保护Java应用的特性,如支持Web应用[8]、线程和并发、缓存机制和测试工具等。与其他安全框架相比,Shiro的安全认证和授权方式比较简洁且容易操作,编写的代码量显著减少。本文主要研究如何将Shiro的授权功能应用在Web网络安全问题中,解决越权访问的问题。
当用户登录系统并进行数据操作访问Web数据库[9]时,可能由于本身系统设计上存在的安全漏洞[10]或者用户权限分配和管理上存在的缺陷,导致授权用户越权访问未授权的数据库资源或者未授权用户非法访问数据库资源。为了阻止这些安全隐患的发生,可在Web应用中集成Shiro安全框架。
首先在Web工程里的web.xml配置文件中定义Shiro Servlet过滤器实现Web应用和Shiro框架的集成。该过滤器会过滤用户发送来的所有请求,根据实际需求执行特定的逻辑判断,当请求满足一定要求时才允许通过,从而保证用户在进行Web数据库访问时,会先对用户的访问权限进行判断,若拥有相应的权限,则执行对应的业务逻辑操作,访问数据库并得到数据。数据库访问流程如图1所示。
图1 基于Shiro的数据库访问流程
授权,其实质就是访问控制[11-12],控制系统用户能够访问Web系统中的哪些资源,能执行哪些操作(如访问页面、编辑数据等),这些一般是通过系统管理员分配给用户的角色和权限来决定的。Shiro支持权限的概念,能友好地与Web系统集成[13],还能够通过运用通配符灵活地对权限进行匹配和检查。目前,Shiro提供以下3种方式的授权:
1)编程式授权。通过编写if/else等一系列Java的授权代码块来实现当前用户的授权操作,例如:
Subject currentUser=SecurityUtils.getSubject();
if (currentUser.hasRole("read")) {
//有角色"read"权限
} else {
//没有角色"read"权限}
2)JSP/GSP标签式授权。在JSP/GSP页面通过相应的标签来实现授权,例如:
3)注解式授权。在执行的Java方法上放置相应的注解来实现系统用户的授权操作(前提是开发的系统需要支持面向切面的编程[14]),例如:
@RequiresPermissions("user:read")
//拥有角色"user"中的"read"权限
void someMethod();
以前访问数据资源时,需要先对访问的url进行权限配置,在spring-shiro.xml中编写大量的Java代码,与最开始的Hibernate框架相同,需要编写大量的hbm配置文件,显得十分冗余,不利于后期维护,因而迫使开发者们逐渐选择注解形式来进行程序的开发和授权等操作。本文采用基于Shiro框架的JSP/GSP标签式授权和注解式授权来实现权限操作,配置非常简洁方便,显著减少了授权部分的代码量,同时还方便后期的管理和维护。
以如下Shiro权限注解为例进行分析:
@RequiresPermissions("user:find","user:add",…)
void someMethod();
注解@RequiresPermissions要求当前登录用户必须同时具备user:find和user:add权限时,才能继续执行注解下面的方法someMethod(),否则系统将会抛出异常AuthorizationException。
在Web系统中,该注解作用于Java方法上,表示对访问这些方法的用户进行访问拦截,当判断出用户拥有该权限时才能继续访问,使得在用户访问后台数据资源时,先判定其是否拥有访问此方法的权限,对阻止越权访问有一定的作用。但在设计Web系统时,可能在开发授权这块考虑不周或者部分业务逻辑代码遗漏权限判断功能,导致用户在未授权的情况下越权访问这部分数据,甚至非法访客通过一些黑客技术来非法访问系统数据库中的数据资源,泄露了客户的个人隐私和一些敏感重要的商业信息,造成无法挽回的损失。
为避免越权访问情况的发生,本文在Shiro提供的权限注解@RequiresPermissions基础上,提出一个作用于Java类上的改进的权限注解。
2.3.1 实现方法
根据Shiro提供的作用于Java方法的权限注解@RequiresPermissions,本文提出一个改进的作用于Java类的权限注解@ClassPermissions,实现步骤为:
1)编写一个ClassPermissions.java,在其中定义基于类的权限注解@ClassPermissions:
public @interface ClassPermissions {
String[] value();}
2)在Spring[15]配置文件spring-shiro.xml中添加:
3)继承AuthorizingAnnotationHandler类,将构造函数中new的对象替换成自己的AOP实现。关键代码如下:
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重写一个自己的,修改权限验证拦截器栈的设置,修改权限验证的拦截器。关键代码如下:
interceptors.add(new ClassAnnotationMethod
Interceptor(resolver));
//自定义
interceptors.add(new ClassAuthorizingAnnotation
MethodInterceptor());
5)实现所需的权限验证拦截器,根据spring所提供的类AuthorizingAnnotationMethodInterceptorl重写一个自己的类。关键代码如下:
publicClassAuthorizingAnnotationMethod-
Interceptor (){
super(new ClassAuthorizingAnnotationHandler());}
public ClassAuthorizingAnnotationMethod-
Interceptor(AnnotationResolver resolver){
super(new ClassAuthorizingAnnotationHandler(),
resolver);}
6)实现所需的权限处理器,继承spring提供的类AuthorizationAttributeSourceAdvisor,实现作用于类的权限注解,获取类上的注解以及类下所有方法的注解。关键代码如下:
private static final Class extends Annotation>[ ] annotationClass=new Class[] {//注解权限
ClassPermissions.class,
RequiresPermissions.class,……};
//匹配带有注解的方法
@Override
public boolean matches(Method m,Classc){
boolean flag = super.matches(m,c);
//若方法上没有权限注解,则获取类上权限注解
if(!flag && isAuthzAnnotationPresent(c) && isWebAnnota tionPresent(m)){
flag = true;}
return flag;}
//查看方法上是否有权限注解
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 类与方法之间的安全作用机制
对集成了Shiro框架的Web系统各个功能模块的url访问都需要角色权限。当用户访问时,后台会根据前端的 url地址定位到所对应方法上的权限注解。spring会先扫描Shiro注解类的matches方法(上述第6个步骤),并通过返回true/false的方式来判断某个方法是否带有Shiro权限注解。若返回true,则再判断当前访问操作是否满足方法上的注解权限;若返回false,表示方法上没有权限注解,则会去获取所属类上的权限注解@ClassPermissions,再判断当前访问操作的权限。
结合上节中实现的作用于Java类上的权限注解@ClassPermissions,本文提出一个基于Shiro标签式授权和注解式授权的权限访问控制算法。
算法基于Shiro标签和注解的访问控制算法
输入访问者的请求request=
输出访问者得到的视图
步骤1根据请求request获得user对应的角色信息,并通过角色ID (useId)获取Shiro中保存的对应的权限信息。
步骤2Shiro过滤器拦截所有的请求,对每个请求进行权限判断。
步骤3若是前端请求,判断相应方法上的JSP/GSP权限标签。若拥有该权限,则跳转至步骤7,否则跳转至步骤8。
步骤4若是后台请求,判断相关Java方法上是否有权限注解。若有,则跳转至步骤5,否则跳转至步骤6。
步骤5若拥有权限注解@RequiresPermissions中标识的权限,则跳转至步骤7,否则跳转至步骤8。
步骤6获取并判断所属Java类上的权限注解@ClassPermissions,若拥有注解中标识的权限,则跳转至步骤7,否则跳转至步骤8。
步骤7允许访问,获取数据库中的匹配信息,然后跳转至步骤9。
步骤8禁止访问,抛出异常。
步骤9输出经过权限过滤后的数据信息。
用户登录时发出请求request=
基于 Shiro的授权流程如下:首先系统会调用Subject.isPermitted("权限串")方法,然后委托给securityManager进行处理,通过securityManager内部的Authorizer(默认是实现ModularRealmAuthorizer)来进行真正的授权处理。ModularRealmAuthorizer会调用Realm的授权方法doGetAuthorizationInfo,从数据库查询权限数据,返回ModularRealmAuthorizer,ModularRealmAuthorizer会调用PermissionResolver进行权限串比对。若从Realm中获取的当前Subject的角色信息(即权限串)与传入的isPermitted("权限串")相匹配,则返回ture,有访问权限;否则返回false,没有访问权限。
对于后台的访问请求,系统会先定位到具体的Java方法上,并查找方法上的权限注解,例如@RequiresPermissions("user:find")。若方法上存在权限注解,则按照上述Shiro的授权流程来进行授权处理;若方法上没有权限注解,则会去获取所在类上的权限注解@ClassPermissions("user:find")再进行授权处理,从而实现对后台访问请求的权限控制。
综上所述,基于权限访问控制的用户授权流程如图2所示。
图2 基于权限访问控制的用户授权流程
3.3.1 安全性
一个集成了Shiro的Web系统,基本每个功能模块的Controller层都有同样的add、list等方法,这些方法不处理业务逻辑,只是把请求从Controller层转到Service层处理完后,将结果再转给相应的视图。此时只需在Controller层编写抽象类,并实现这些方法,再使用@RequestMapping标记此类。其他需要实现增删改查功能的Controller都继承此抽象类,每个类只需编写自己的视图地址即可。此时,需要注解@RequiresPermissions能实现对类的注解。但是,根据文献[5]和Apache Shiro官网可知,该注解只能作用在方法上,导致其无法在Controller层的抽象类上实现权限验证功能。
本文提出的权限注解@ClassPermissions可针对某个类进行注解,解决了上述技术难题。其相应的权限访问控制算法与Apache Shiro官网提供的@RequiresPermissions的权限验证过程相比,相当于增加了3.1节中步骤4和步骤6的判断,避免了开发过程中某些方法上缺少权限验证的问题,提高了系统的安全性。同时,不再需要对Controller层的所有方法进行注解,节省了大量的开发时间,使得授权更加灵活,便于管理和维护。
3.3.2 有效性
本文采用某公司的角色信息管理系统进行效果测试。该系统类似于文献[13]中的系统,其采用了B/S体系结构,以Spring+SpringMVC+Hibernate开源框架设计开发,整合了Shiro框架,使用MySQL数据库。
创建一个没有任何操作权限的普通用户,测试100个url:http://localhost:8080/spring/rest/…①…/…②…,其中:①表示系统的功能模块名;②表示每个模块中增删改查等方法的方法名,观察并记录网页的输出结果;然后在各个功能模块的Controller层上实现权限注解@ClassPermissions,再观察输出结果。实验结果对比如表1所示。由对比数据可知,实现类上的注解后可有效地阻止越权访问。
表1 访问结果对比
3.3.3 普适性
只要开发的Web应用系统支持面向切面的编程,即可很容易地实现作用于Java类的权限注解@ClassPermissions,从而实现方法和类上的权限控制。因此,本文算法在实际开发应用中具有很好的普遍适用性。
针对Web系统中的越权访问问题,本文提出一种基于Shiro安全框架的权限访问控制算法,通过协同使用作用于Java方法和类上的权限注解,实现整个Web系统后台的授权。与之前仅采用Shiro本身提供的权限注解来实现系统授权相比,该算法可有效阻止越权访问的发生,提高系统的安全性,同时降低开发者工作的复杂度,使操作更加灵活方便。下一步工作将继续运用Shiro和其他安全框架来解决Web系统中存在的各种安全问题。