如何搭建沙盒环境

Posted

技术标签:

【中文标题】如何搭建沙盒环境【英文标题】:How to build a sandbox environment 【发布时间】:2016-10-11 09:32:27 【问题描述】:

您好 SecurityManager 专家 ;-)

我编写了一个小型插件框架,它使用单独的隔离类加载器加载插件。要成功取消部署插件,重要的是确保应用程序不持有对插件类加载器加载的类的引用。

Java 中有一个称为shutdown hook 的功能,它使客户端代码能够注册一个线程,该线程在JVM 被请求关闭时执行。这意味着可能会有 JVM 持有对插件类加载器加载的类的引用。

我的第一次尝试是安装一个拒绝添加关闭挂钩的 SecurityManager。这完全有效,并且拒绝了所有添加关闭挂钩的尝试。有趣的是,我意识到并不是插件本身想要添加关闭挂钩。该插件只是通过加载字体来触发 AWT/Swing 内部(精确:SunFontManager)。 SunFontManager 反过来添加了关闭钩子,以便在 JVM 退出时进行一些内部处理。我不想否认插件加载了字体(因为它必须),我不想否认 Java 内部添加他们的东西。

对我来说,似乎我必须授予代码更多的权限(如果 sun.*|java.*|javax.* 包在堆栈跟踪中..uuuhmm...ugly),而不是正常情况下。但这打破了安全框架的约定,它假定客户端代码不能比调用它的代码拥有更多的权限。

我如何区分不是插件代码而是Java内部想要做某事?或者有没有其他方法可以确保不通过否认来破坏内部?

发生情况的示例

为了让事情更清楚,这是我的应用程序中实际发生的情况。我安装了一个自定义的 SecurityManager,它应该拒绝由插件直接执行的addShutdownHook,但如果作为 Java 内部类的副作用执行,则不应拒绝这些调用。

上下文: 调用: ---------------------------------------------- AppClassloader 应用程序 | PluginClassloader PluginObject.init() | AppClassloader 字体.create() | AccessController.doPrivileged(...) Runtime.addShutdownHook(...)

SecurityManager 是使用 System.setSecurityManager() 安装的,并且这样做:

@Override
public void checkPermission(java.security.Permission perm) 
  if (perm.getName().equals("shutdownHooks")) 
    if (threadContextClassLoaderIsPluginClassLoader()) 
      throw new SecurityException("Installing shutdown hooks is not allowed.");
    
  

虽然addShutdownHookdoPrivileged-block 中被调用,但我的SecurityManager 被调用。我错过了什么吗?我应该自己检查特权上下文吗?

【问题讨论】:

我找到了这篇文章 ***.com/questions/2233761/...,它解决了确切的问题。如果我做对了,如果在 AccessController.doPrivileged() 块中完成 addShutdownHook 方法应该会成功,因为类 java.awt.Font 具有 AppClassloader 的所有权限。尽管在 doPrivilieged() 块中调用,我仍然发现我的 SecurityManager 被要求获得权限。我应该如何在java.lang.SecurityManager.checkPermissi‌​on(Permission) 中检查这个? 如果你在 doPrivileged 中调用它,SecurityManager 仍然需要检查“特权”类是否有权注册关闭挂钩。 【参考方案1】:

我发现我完全误解了 Java 安全概念。我想为插件代码构建一个沙箱,以便它始终在受限制的环境中运行,我可以控制它拥有的权限。应用程序应始终在授予完全访问权限的情况下运行。

以下是如何为由单独的类加载器加载的类构建沙箱:

    使用默认的 SecurityManager 如果您发现自己实现了自己的 java.lang.SecurityManager 以根据权限允许或拒绝操作,那么您可能做错了!你只需要安装默认的java.lang.SecurityManager 这样做System.setSecurityManager(new SecurityManager());。现在,您可以使用 Java 默认值获得有效的访问控制。

    将所有权限授予应用程序 加载插件的应用程序应获得完全访问权限。这是因为我们相信自己。我通过使用授予所有访问权限的策略文件启动我的应用程序来解决这个问题。我认为应用程序启动时需要策略文件,因为 AppClassLoader 需要为类创建正确的保护域。使用 JavaVM 参数 java.security.policy=<URL-TO-POLICY> 启动主应用程序,其中 <URL-TO-POLICY 指向以下策略文件:

    grant permission java.security.AllPermission; ;

    自定义策略实施我们需要在应用启动后安装自定义策略。我们希望将主应用程序的权限(在授予完全访问权限的情况下运行)和插件(我们想要限制哪些权限)分开。插件应在具有手动选择权限的沙箱中运行。为了实现这一点,这里是自定义策略实现:

    class SandboxPolicy extends Policy 
    @Override
    
      public PermissionCollection getPermissions(ProtectionDomain domain) 
        // Decide if the plugin permissions are needed or full access can be granted using all permissions.
        if (isPlugin(domain)) 
          return pluginPermissions();
         else 
          return applicationPermissions();
        
      
    
      private boolean isPlugin(ProtectionDomain domain) 
        // Identify the classloader of the protection domain
        // The PluginClassLoader is assumed to be the one that loaded
        // the plugin
        return domain.getClassLoader() instanceof PluginClassLoader;
      
    
      private PermissionCollection pluginPermissions() 
        // Empty permissions = No permissions
        // This is not the point to add plugin permissions
        return new Permissions();
      
    
      private PermissionCollection applicationPermissions() 
        // Grant full access to the application
        Permissions permissions = new Permissions();
        permissions.add(new AllPermission());
        return permissions;
      
    
    

    每个类加载器的保护域 类加载器负责为每个加载源代码创建一个ProtectionDomain。保护域指定将授予代码的权限。我们必须修改类加载器添加到保护域的权限集。为此,让您的 PluginClassloader 扩展 java.security.SecureClassLoader。然后通过以下方式覆盖方法java.security.SecureClassLoader.getPermissions(CodeSource)

    @Override
    protected PermissionCollection getPermissions(CodeSource codeSource)
    
        PermissionCollection pc;
        // The SecureClassloader per default grants access to read resources from the source JAR.
        // This is useful. Call super to get those permissions:
        pc = super.getPermissions(codeSource);
        // At this point you can extend permissions.
        // For example grant read access to a file.
        pc.add(new FilePermission("path\\file", "read"));
        return (pc);
    
    

注意:查看政策实施并查看我的评论// This is not the point to add plugin permissionsAccessController 询问该策略是否可以授予权限。如果在此处添加权限,您可以从调用者那里获得的信息非常有限。我不建议在这里计算和添加任何权限。类加载器是您可以根据代码源添加权限的地方。我建议在这里添加权限。它们在保护域中变得可见,您可以轻松观察 AccessController 对这些保护域的操作。

特权操作

我的问题还有一部分要回答。

有趣的是,我意识到并不是插件本身想要添加关闭挂钩。该插件只是通过加载字体来触发 AWT/Swing 内部(准确地说:SunFontManager)。

有些操作将作为插件代码的副作用执行。 org.plugin.A 类可以触发 Swing/AWT 内部添加关闭挂钩。在这种情况下,我们不想否认这一点。 Java 类在应用程序的范围内,应该获得完全访问权限。插件代码可能会受到限制,直接添加关闭挂钩应该会失败并显示SecurityException。这是java.security.AccessController.doPrivileged(PrivilegedAction<T>) 的常见用例。特权操作确保AccessController 只考虑最后一个堆栈帧的保护域。 AWT/Swing 将其关闭挂钩添加到特权操作中,因此被允许这样做,因为执行此操作的类的保护域设置具有完全访问权限。请参阅java.security.AccessController 的任何文档以了解有关特权操作的更多信息。

【讨论】:

以上是关于如何搭建沙盒环境的主要内容,如果未能解决你的问题,请参考以下文章

flask环境搭建

如何在iphone中制作沙盒环境

如何在 zoho crm 中将沙盒编辑代码部署到生产环境

无效的产品标识符:如何验证 iOS 沙盒环境

应用内购买:需要登录【环境:沙盒】

Plaid - 在沙盒环境中,我如何模拟正在发生的新事务并在 Webhook 中获取它