Java表达式注入案例

wen java案例 2

本文目录导读:

Java表达式注入案例

  1. Java表达式注入案例
  2. 常见案例
  3. 检测工具

Java表达式注入案例

什么是表达式注入?

表达式注入是一种安全漏洞,当应用程序将用户输入直接传递给表达式求值引擎(如SpEL、OGNL、MVEL等)进行解析执行时,攻击者可以注入恶意表达式导致远程代码执行。

常见案例

Spring Expression Language (SpEL) 注入

漏洞代码示例:

import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class SpELInjectionExample {
    public static void main(String[] args) {
        String userInput = "T(java.lang.Runtime).getRuntime().exec('calc.exe')";
        ExpressionParser parser = new SpelExpressionParser();
        // 危险:直接解析用户输入
        Object result = parser.parseExpression(userInput).getValue();
        System.out.println(result);
    }
}

修复方案:

public class SpELFixExample {
    public static void main(String[] args) {
        String userInput = "T(java.lang.Runtime).getRuntime().exec('calc.exe')";
        ExpressionParser parser = new SpelExpressionParser();
        // 使用SimpleEvaluationContext限制可访问的类和对象
        EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
        // 或者直接在表达式中限制可访问的变量
        try {
            // 设置白名单,只允许访问特定对象
            StandardEvaluationContext safeContext = new StandardEvaluationContext();
            safeContext.setRootObject(new SafeObject());
            Object result = parser.parseExpression(userInput).getValue(safeContext);
        } catch (Exception e) {
            // 安全的错误处理
            System.err.println("非法表达式: " + e.getMessage());
        }
    }
    static class SafeObject {
        private String name;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
}

OGNL (Object-Graph Navigation Language) 注入

常见于Struts2框架

漏洞代码示例:

import ognl.Ognl;
import ognl.OgnlContext;
public class OGNLInjectionExample {
    public static void main(String[] args) {
        String userInput = "#a=(new java.lang.ProcessBuilder(new java.lang.String[]{'cmd','/c','calc'})).start()";
        OgnlContext context = new OgnlContext();
        try {
            // 危险:直接解析用户输入
            Object expression = Ognl.parseExpression(userInput);
            Ognl.getValue(expression, context, new Object());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

修复方案:

import ognl.ASTProperty;
import ognl.Node;
import ognl.Ognl;
import ognl.OgnlContext;
public class OGNLFixExample {
    private static final String[] ALLOWED_PROPERTIES = {"name", "age", "email"};
    public static boolean validateExpression(String expression) {
        try {
            Node node = Ognl.parseExpression(expression);
            return validateNode(node);
        } catch (Exception e) {
            return false;
        }
    }
    private static boolean validateNode(Node node) {
        if (node instanceof ASTProperty) {
            String property = ((ASTProperty) node).getProperty(null).toString();
            return Arrays.asList(ALLOWED_PROPERTIES).contains(property);
        }
        // 递归验证子节点
        for (int i = 0; i < node.jjtGetNumChildren(); i++) {
            if (!validateNode(node.jjtGetChild(i))) {
                return false;
            }
        }
        return true;
    }
    public static Object safeGetValue(String expression, Object root) {
        if (!validateExpression(expression)) {
            throw new SecurityException("非法表达式: " + expression);
        }
        OgnlContext context = new OgnlContext();
        try {
            Node node = Ognl.compileExpression(context, root, expression);
            return Ognl.getValue(node, context, root);
        } catch (Exception e) {
            throw new RuntimeException("表达式执行失败", e);
        }
    }
}

MVEL (MVFLEX Expression Language) 注入

漏洞代码示例:

import org.mvel2.MVEL;
public class MVELInjectionExample {
    public static void main(String[] args) {
        String userInput = "Runtime.getRuntime().exec('calc')";
        // 危险:直接执行用户输入
        Object result = MVEL.eval(userInput);
        System.out.println(result);
    }
}

修复方案:

import org.mvel2.MVEL;
import org.mvel2.ParserContext;
public class MVELFixExample {
    public static void main(String[] args) {
        String userInput = "Runtime.getRuntime().exec('calc')";
        // 创建安全的解析器上下文
        ParserContext context = new ParserContext();
        // 禁用危险的函数和类
        context.addImport("Math", Math.class);
        context.setAllowNakedMaptrixAccess(false);
        context.setStrictTypeEnforcement(true);
        // 定义白名单类
        Set<String> allowedClasses = new HashSet<>();
        allowedClasses.add("java.lang.Math");
        allowedClasses.add("java.lang.String");
        context.addClassImport("String", String.class);
        // 使用白名单验证
        if (isValidExpression(userInput, allowedClasses)) {
            try {
                Object result = MVEL.eval(userInput, context);
                System.out.println("结果: " + result);
            } catch (Exception e) {
                System.err.println("表达式执行错误: " + e.getMessage());
            }
        } else {
            System.err.println("包含不允许的类或方法");
        }
    }
    private static boolean isValidExpression(String expression, Set<String> allowedClasses) {
        // 检查是否包含危险的方法调用
        String[] dangerousPatterns = {
            "Runtime", "ProcessBuilder", "exec", "invoke",
            "ClassLoader", "getClass", "forName"
        };
        for (String pattern : dangerousPatterns) {
            if (expression.contains(pattern)) {
                return false;
            }
        }
        return true;
    }
}

自定义表达式引擎注入

import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
public class ScriptEngineInjectionExample {
    public static void main(String[] args) {
        String userInput = "var proc = new java.lang.ProcessBuilder('calc').start()";
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");
        try {
            // 危险:直接执行用户输入的脚本
            engine.eval(userInput);
        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }
}

修复方案(使用沙箱环境):

import javax.script.*;
import java.util.*;
public class SecureScriptEngineExample {
    public static void main(String[] args) {
        String userInput = "var proc = new java.lang.ProcessBuilder('calc').start()";
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");
        // 创建安全上下文
        Bindings bindings = engine.createBindings();
        bindings.put("safeMath", new MathHelper());
        // 使用简单的数学运算白名单
        SimpleScriptContext context = new SimpleScriptContext();
        context.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
        // 设置写入限制
        StringWriter writer = new StringWriter();
        context.setWriter(writer);
        try {
            // 使用预编译和验证
            if (isMathExpression(userInput)) {
                Object result = engine.eval(userInput, context);
                System.out.println("结果: " + result);
            } else {
                System.err.println("只允许数学表达式");
            }
        } catch (ScriptException e) {
            System.err.println("脚本执行错误: " + e.getMessage());
        }
    }
    private static boolean isMathExpression(String input) {
        // 只允许基本的数学运算
        return input.matches("[0-9+\\-*/()\\s.]+");
    }
    // 提供安全的方法
    static class MathHelper {
        public double add(double a, double b) { return a + b; }
        public double subtract(double a, double b) { return a - b; }
        public double multiply(double a, double b) { return a * b; }
        public double divide(double a, double b) { 
            if (b == 0) throw new ArithmeticException("除数不能为0");
            return a / b; 
        }
    }
}
  1. 输入验证:严格验证和过滤用户输入
  2. 使用安全上下文:使用限制性的求值上下文
  3. 白名单机制:只允许访问特定的类和方法
  4. 禁用动态执行:尽可能避免使用表达式引擎
  5. 最小权限原则:应用程序只拥有必要的权限
  6. 使用安全库:选择有安全特性支持的表达式引擎版本
  7. 代码审查:定期进行安全代码审查
  8. 日志记录:记录所有表达式执行操作

检测工具

  • FindSecBugs:用于检测Java安全漏洞
  • Snyk:开源组件安全扫描
  • OWASP Dependency Check:检查依赖项漏洞
  • 静态代码分析工具:如SonarQube、Checkmarx等

避免表达式注入的最佳实践是在设计阶段就考虑安全性,避免将用户输入直接用于表达式求值,如果必须使用,要实施严格的输入验证和执行环境限制。

抱歉,评论功能暂时关闭!