You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java Rhino安全Context中指定允许访问的Java类?

如何在Rhino JavaScript沙箱中允许访问指定Java类(同时限制其他类)

我明白你想要的是一个严格受控的JavaScript沙箱——用Context.initSafeStandardObjects()打下安全基础,但又得让JS能访问你指定的几个Java类,绝对不能像initStandardObjects()那样开放全部权限。这在Rhino的沙箱场景里是个很典型的需求,我来给你拆解下具体怎么做。

核心思路

initSafeStandardObjects()会生成一个默认屏蔽所有Java类访问权限的全局对象,我们的目标就是手动把你指定的Java类/方法添加到这个全局对象中,同时通过额外的拦截机制,确保其他未授权的类完全无法被访问。

具体实现步骤

1. 初始化安全全局对象

先通过initSafeStandardObjects()创建上下文和全局作用域,这是保证沙箱基础安全的第一步:

Context cx = Context.enter();
try {
    Scriptable scope = cx.initSafeStandardObjects();
    // 后续添加指定类的操作都在这里进行
} finally {
    Context.exit();
}

2. 向全局作用域暴露指定Java类

要让JS能访问Java类,需要用Context.javaToJS()把Java类/对象转换成Scriptable类型,再挂载到全局作用域里。这里有两种常见的实现方式,安全性和灵活性各有侧重:

方式一:直接暴露单个Java类

如果你确定某个类的所有方法都可以开放,直接把类本身挂载到全局作用域即可。比如允许JS访问java.util.ArrayList:

// 获取要暴露的目标Java类
Class<?> arrayListClass = java.util.ArrayList.class;
// 转换为JS可识别的对象
Scriptable arrayListObj = cx.javaToJS(arrayListClass, scope);
// 挂载到全局作用域,JS中可通过`ArrayList`直接调用
scope.put("ArrayList", scope, arrayListObj);

对应的JS使用示例:

let myList = new ArrayList();
myList.add("hello sandbox");
print(myList.size()); // 输出1

方式二:通过包装类暴露有限功能

如果不想开放类的全部方法(比如避免危险的getClass()等反射入口),最好自定义一个包装类,只暴露你需要的功能,再把包装类的实例挂载到全局作用域:

// 自定义安全包装类,仅开放必要方法
public class SafeListWrapper {
    private ArrayList<String> innerList = new ArrayList<>();
    
    public void addItem(String item) {
        innerList.add(item);
    }
    
    public int getCount() {
        return innerList.size();
    }
}

// 在沙箱中暴露包装类实例
SafeListWrapper safeList = new SafeListWrapper();
Scriptable safeListObj = cx.javaToJS(safeList, scope);
scope.put("SafeList", scope, safeListObj);

对应的JS使用示例:

SafeList.addItem("controlled access");
print(SafeList.getCount()); // 输出1

这种方式能最大程度缩小攻击面,是更安全的选择。

3. 用ClassShutter彻底拦截未授权类

即使你只暴露了指定类,JS仍可能通过反射绕过限制。这时需要用Context.setClassShutter()设置一个拦截器,只有你明确授权的类才能被访问:

cx.setClassShutter(new ClassShutter() {
    @Override
    public boolean visibleToScripts(String className) {
        // 仅允许以下类被JS访问
        return className.equals("java.util.ArrayList") 
            || className.equals("com.yourpackage.SafeListWrapper");
    }
});

这个拦截器会检查所有JS对Java类的访问请求,只有返回true的类才能被加载,彻底杜绝反射绕过的可能。

完整示例代码

把上述步骤整合起来,一个完整的受控沙箱实现如下:

import org.mozilla.javascript.*;

public class ControlledJsSandbox {
    public static void main(String[] args) {
        Context cx = Context.enter();
        try {
            // 设置JS语言版本为ES5,保证兼容性
            cx.setLanguageVersion(Context.VERSION_ES5);
            
            // 初始化安全全局对象
            Scriptable scope = cx.initSafeStandardObjects();
            
            // 设置类访问拦截器
            cx.setClassShutter(new ClassShutter() {
                @Override
                public boolean visibleToScripts(String className) {
                    return className.equals("java.util.ArrayList") 
                        || className.equals("com.yourpackage.SafeListWrapper");
                }
            });
            
            // 暴露指定Java类到全局作用域
            Class<?> arrayListClass = java.util.ArrayList.class;
            Scriptable arrayListObj = cx.javaToJS(arrayListClass, scope);
            scope.put("ArrayList", scope, arrayListObj);
            
            // 测试沙箱内的JS代码
            String jsCode = "let list = new ArrayList(); list.add('test'); print(list.size());";
            cx.evaluateString(scope, jsCode, "SandboxTest", 1, null);
            
        } finally {
            Context.exit();
        }
    }
}

关键安全注意事项

  • 绝对不要暴露java.lang.Class、java.lang.reflect相关类,否则JS可以通过反射获取任何系统类。
  • 优先使用包装类暴露功能,而非直接开放原生Java类,减少不必要的权限。
  • 测试时要尝试各种绕过手段(比如通过暴露类的方法间接获取其他类实例),确保沙箱没有漏洞。

内容的提问来源于stack exchange,提问作者František Jeřábek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:58:15