Java中的动态代码评估-聪明还是草率?


30

我正在尝试为我的应用程序在Java中创建一个灵活的ACL框架。

许多ACL框架都建立在规则白名单上,其中规则的形式为owner:action:resource。例如,

  • “ JOHN可以查看资源FOOBAR-1”
  • “ MARY可以查看资源FOOBAR-1”
  • “ MARY可以编辑资源FOOBAR-1”

这很有吸引力,因为规则可以轻松地序列化/持久化到数据库。但是我的应用程序具有复杂的业务逻辑。例如,

  • “部门1中具有5年以上资历的所有用户都可以查看资源FOOBAR-1,否则未经授权”
  • “部门2中的所有用户,如果日期是2016年3月15日之后,则可以查看资源FOOBAR-2,否则未经授权”

乍一想,设计一个可以处理无限复杂规则的数据库模式将是一场噩梦。因此,似乎我需要将它们“烘焙”到编译的应用程序中,为每个用户评估它们,然后根据评估结果生成owner:action:resource规则。 我想避免将逻辑烘焙到已编译的应用程序中。

因此,我正在考虑以谓词:action:resource的形式表示规则,其中谓词是确定是否允许用户的布尔表达式。该谓词将是Java的Rhino引擎可以评估的JavaScript表达式的字符串。例如,

  • return user.getDept() == 1 && user.seniority > 5;

这样,可以轻松地将谓词保存到数据库中。

这很聪明吗?这是草率的吗?这是花哨的吗?这是过度设计的吗?这样安全吗(显然,Java可以将Rhino引擎沙箱化)。


8
与将逻辑放入已编译的应用程序相比,尝试将这些业务规则推入数据库有什么好处?
Winston Ewert 2015年

6
@WinstonEWert通过对规则进行外部化,可以在更改,添加或删除规则时无需重新编译和重新分发应用程序。
Twittopher


2
有趣的问题!我希望看到一个答案,该答案不那么关注安全性,而是关注这种解决方案的维护,可靠性和易用性方面。
奥利弗2015年

6
这听起来与Outlook电子邮件规则类似,后者实质上是可由用户配置的规则引擎。

Answers:


37

将动态数据插入实现语言的解释器中通常不是一个好主意,因为它将数据损坏的可能性升级为恶意应用程序接管的可能性。换句话说,你自己的方式走出去创造一个代码注入漏洞。

您可以通过规则引擎特定领域语言(DSL)更好地解决您的问题。看看这些概念,就不需要重新发明轮子了。


16
但是在这里,JavaScript是否不可以用作类似DSL的脚本语言?我们设置必要的数据(只读),将代码段包装到函数中,然后对其进行安全评估。由于代码除了返回布尔值之外什么也不能做,因此这里没有恶意的机会。
阿蒙(Amon)2015年

6
@Twittopher拖动整个JavaScript引擎来评估某些谓词似乎仍然像1)完全过大,2)冒险和3)容易出错。举例来说,您使用==而不是===您的示例。当所有规则都应该总是终止时,您真的要提供图灵完整性吗?与其跳过障碍物来确保Java和JavaScript之间的所有交互都是洁净的,不如编写像Kilian建议的那样简单的解析器和解释器,为什么呢?根据您的需求和安全性进行定制会容易得多。使用ANTLR之类的东西。
Doval 2015年

6
@Doval编写小型DSL并不是完全科学的技术,我可以在3到5天内用一种简单的语言来学习。但这似乎是1)彻底的矫,过正,2)冒险,以及3)容易出错。您是否真的想编写整个迷你语言?如果某些业务规则比预期的要复杂,并且需要功能全面的语言怎么办?您是否患有“非此处发明的综合征”?除了重新发明轮子,您为什么不使用现有语言?使用已经经过考验的语言要容易得多。
阿蒙2015年

14
@amon发生这种情况时,您会像Killian所说的那样找到一个真正的规则引擎(不是JavaScript)。将两种方法的风险均等是误导的。只需花一点时间就可以破坏您为确保图灵完整语言的翻译而付出的所有努力;这是一个减法过程。偶然使小型DSL变得危险起来要困难得多。这是一个加法过程。您可能会犯的那种错误是错误地解释了语法树,并且可以对它进行单元测试。您可能不会不小心将解释器的格式设置为硬盘驱动器。
Doval 2015年

4
@amon:即使js代码段可能没有副作用,它也可以选择返回布尔值:while (true) ;
Bergi 2015年

44

我这样做了,建议您不要这样做。

我所做的就是在Lua中编写所有业务逻辑,并将该Lua脚本存储在数据库中。当我的应用程序启动时,它将加载并执行脚本。这样,我可以在不分发新二进制文件的情况下更新应用程序的业务逻辑。

我总是发现进行更改时总是需要更新二进制文件。Lua脚本中有一些更改,但是我总是要进行一系列更改,因此我几乎总是最终不得不对二进制文件进行某些更改以及在Lua脚本中进行一些更改。我一直无法避免分发二进制文件的想象并没有成功。

我发现更有用的是简化二进制文件的分发。我的应用程序会在启动,下载和安装任何更新时自动检查更新。因此,我的用户始终使用我推送的最新二进制文件。二进制文件的更改和脚本的更改之间几乎没有区别。如果我再做一次,我将付出更多的努力来使更新无缝进行。


3

我不会在数据库中包含代码。但是您可以通过使数据库包含函数名称,然后使用反射来调用它们来执行类似的操作。添加新条件时,必须将其添加到代码和数据库中,但是可以组合传递给它们的条件和参数来创建非常复杂的评估。

换句话说,如果您已为部门编号,则很容易进行UserDepartmentIs检查和TodayIsAfter检查,然后将它们合并为Department = 2和Today> 03/15/2016。如果然后要进行TodayIsBefore检查,以便可以在权限结束日期之前,则必须编写TodayIsBefore函数。

我尚未针对用户权限执行此操作,但已针对数据验证执行了此操作,但它应该可以工作。


2

XACML是您真正想要的解决方案。它是一种规则引擎,仅专注于访问控制。XACML是OASIS定义的标准,它定义了三个部分:

  • 建筑
  • 一种策略语言(这确实是您想要的)
  • 请求/响应方案(您如何请求授权决定)。

架构如下:

  • 策略决策点(PDP)是体系结构的核心部分。它是根据一组已知策略评估传入授权请求的组件
  • 策略执行点(PEP)是保护您的应用程序/ API /服务的代码段。PEP拦截业务请求,创建XACML授权请求,将其发送给PDP,接收回响应,并在响应内部执行决策。
  • 策略信息点(PIP)是可以将PDP连接到外部数据源(例如LDAP,数据库或Web服务)的组件。当PEP发送请求时,例如“ Alice可以查看文档#12?”时,PIP会派上用场。PDP的政策要求使用者的年龄。PDP将询问PIP“给我爱丽丝的年龄”,然后将能够处理这些政策。
  • 策略管理点(PAP)是管理整个XACML解决方案(定义属性,编写策略和配置PDP)的地方。

可扩展访问控制标记语言-XACML体系结构

这是您的第一个用例:

/*
 * All users in department 1 with over 5 years of seniority can VIEW resource FOOBAR-1, else not authorized
 * 
 */
 policy departmentOne{
    target clause department == 1
    apply firstApplicable
    /**
     * All users in department 1 with over 5 years of seniority can VIEW resource FOOBAR-1, else not authorized
     */
    rule allowFooBar1{
        target clause resourceId=="FOOBAR-1" and seniority>=5 and actionId=="VIEW"
        permit
    }
    rule denyOtherAccess{
        deny
    }

 }

您的第二个用例是:

 /*
  * "All users in department 2, if the date is after 03/15/2016, can VIEW resource FOOBAR-2, else not authorized"
  *  
  */
  policy departmentTwo{
    target clause department == 1
    apply firstApplicable
    rule allowFooBar2{
        target clause resourceId=="FOOBAR-1" and seniority>=5 and currentDate>"2016/03/15":date and actionId=="VIEW"
        permit
    }
    rule denyOtherAccess{
        deny
    }
  }

您可以通过使用引用将两个用例组合到一个策略中:

  policyset global{
    apply firstApplicable
    departmentOne
    departmentTwo
  }

大功告成!

您可以从以下网站阅读有关XACML和ALFA的更多信息:


0

您在这里真正想要的是XACML。它几乎可以为您提供所需的一切。您不必完全将所有角色完全分开来实现完整的体系结构...如果您只有一个应用程序,则可以通过balana将PDP和PEP集成到您的应用程序中,而PIP则可以您现有的用户数据库是。

现在,在应用程序中需要授权的任何地方,您都将创建一个XACML请求,其中包含用户,操作和上下文,XACML引擎将根据您编写的XACML策略文件做出决定。这些策略文件可以保存在数据库或文件系统中,也可以保存在任何要保留配置的地方。Axiomatics是XACML XML表示法ALFA的一个很好的替代方案,它比原始XML更易于阅读,并且有一个Eclipse插件可以根据ALFA策略生成XACML XML。


1
这如何回答所提问题?
蚊蚋

他专门尝试实现外部配置的授权系统。XACML是随时可以使用的外部配置的授权系统,可以很好地涵盖他的特定用例。我承认,对于动态代码执行这一更一般的问题,这可能不是一个很好的答案,但对于他的特定问题,这是一个很好的解决方案。
gregsymons 2015年

0

我们在我目前的公司中做到了这一点,我们对结果感到非常满意。

我们的表达式是用js编写的,我们甚至使用它们来限制用户通过查询ElasticSearch可以获得的结果。

诀窍是确保有足够的信息来做出决定,以便您可以真正编写所需的任何烫发而无需更改代码,但同时又要保持快速。

我们实际上并不担心代码注入攻击,因为权限是由不需要攻击系统的人编写的。像while(true)示例一样,这同样适用于DOS攻击。系统管理员不需要这样做,他们可以删除所有人的权限...

更新:

作为组织的中央身份验证管理点,诸如XACML之类的东西似乎更好。我们的用例略有不同,因为我们的客户通常没有IT部门来运行所有这些。我们需要一些自成体系的东西,但是试图保留尽可能多的灵活性。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.