由Fortify扫描结果而引发的java安全模型分析
最近公司在搞security audit,项目里的applet被Fortify扫描后报了一个安全隐患,具体信息如下:
其实是可以正常执行的, 但如果加了一条
就会抛出异常, 原因就像前面说的, 本地默认安全管理器是没有开启的,所以我们的程序有所有权限,但一旦你打开了SecurityManager,你就失去了这些权限,需要重新获得才可以,需要用到权限提升方法doProvileged,整个Security Manager以及访问控制的运行机制,还是比较复杂的,一时也说不清, 请参考http://blog.jobbole.com/31940/, 更具体的还要靠自己慢慢探索了。
Abstract: Non-final methods that perform security checks can be overridden in ways that bypass security checks. Explanation: If a method is overriden by a child class, the child class can bypass security checks in the parent class. Example: In the following code, doSecurityCheck() performs a security check and can be overriden by a child class. public class BadSecurityCheck { private int id; public BadSecurityCheck() { doSecurityCheck(); id = 1; } protected void doSecurityCheck() { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new SomePermission("SomeAction")); } } }
这个问题就是说我在applet类的init()方法代码里用到了AccessController.checkPermission()方法,而由于我的类是public非final的,所以hacker们可以自己写一个子类继承后重写init方法,绕过安全检查过程去干一些坏事。
首先我觉得这警告有一定道理,所以打算把类改成final的,但是我又想不通一个applet如何继承重写以及会有什么坏事可干,因为我的程序init()入口处是这样的:
//check file permission Permission sockPerm=new SocketPermission("*","accept,connect,listen"); Permission filePerm=new FilePermission("<<ALL FILES>>","read"); // what if (in linux) the user cannot read all files try{ AccessController.checkPermission(sockPerm); AccessController.checkPermission(filePerm); // the applet cannot successfully run if this failed }catch(AccessControlException ace){ this.hintText=LocaleUtils.getResourceValue("mainpanel.hinttext.unauthorized"); permissionAllowed=false; }在applet启动时先去检查自己是否有相关权限去读客户端的文件,如果没有就提示用户。所以我没想到这会有什么安全隐患,然后我查阅了java的相关资料,试着去了解java的安全机制到底是怎样的。
首先,只针对java applet,JVM安全策略是这样的:
1. 作为从网络下载到的java代码,applet默认是不受信任的,在客户端没有任何读写权限,完全运行在沙箱里。
2. 为了使applet可以和客户本地文件交互,可以为applet进行签名,签过名的applet有读写文件的权利,但是如果该签名是非信任的,也就是没有认证的,会在加载applet时弹出对话框提示用户,只有用户允许才能获得权限。
3. 如果是认证可信签名,则可以完全正常允许。
了解了applet的权限控制后,又进一步了解了java的安全机制。实际上, Java 将执行程序分成本地和远程两种,本地代码默认视为可信任的,而远程代码(如applet)则被看作是不受信的。对于授信的本地代码,可以访问一切本地资源。
这里的权限控制,是由java安全管理器(Security Manager)管理的,远程代码JVM自动启动管理,而本地默认不启动。
这里只做一个最简单和最基础的演示, 比如我们在平时写java代码文件操作时,很少考虑权限问题,以及为什么我们很少用的AccessController,因为它默认是不开启的,如:
try { // try to create a file at $path File fs = new File("foo"); fs.createNewFile(); } catch (AccessControlException e) { e.printStackTrace(); }
其实是可以正常执行的, 但如果加了一条
System.setSecurityManager(new SecurityManager()); try { // try to create a file at $path File fs = new File("foo"); fs.createNewFile(); } catch (AccessControlException e) { e.printStackTrace(); }
就会抛出异常, 原因就像前面说的, 本地默认安全管理器是没有开启的,所以我们的程序有所有权限,但一旦你打开了SecurityManager,你就失去了这些权限,需要重新获得才可以,需要用到权限提升方法doProvileged,整个Security Manager以及访问控制的运行机制,还是比较复杂的,一时也说不清, 请参考http://blog.jobbole.com/31940/, 更具体的还要靠自己慢慢探索了。