Java EL and Spel表达式注入漏洞成因和一些细节
字数 2039 2025-08-10 10:14:26

Java EL 和 SpEL 表达式注入漏洞深度解析

1. EL (Expression Language) 基础

1.1 EL 背景与特点

EL (Expression Language) 最初是为了简化 JSP 开发而设计的,主要特点包括:

  • 可直接访问 JSP 内置对象 (page, request, session, application)
  • 提供丰富的运算符(关系、逻辑、算术等)
  • 支持扩展函数(可调用 Java 类的静态方法)
  • 简化数据访问方式

1.2 EL 基本语法

EL 表达式语法格式:${expression}

数据存取方式:

  • 点号表示法:${user.name}
  • 方括号表示法:${user["name"]}

特殊情况必须使用方括号:

  1. 属性名称包含特殊字符(如 .- 等非字母数字字符)
  2. 需要动态取值时

1.3 EL 隐式对象

对象 等效 Java 代码 描述
pageContext PageContext JSP 页面上下文
param request.getParameter() 获取单个请求参数
paramValues request.getParameterValues() 获取请求参数数组
header request.getHeader() 获取单个请求头
headerValues request.getHeaders() 获取请求头数组
cookie request.getCookies() 获取 cookie

1.4 禁用 EL 表达式

单个 JSP 文件禁用

<%@page isELIgnored="true" %>

全局禁用(web.xml 配置):

<jsp-config>
    <jsp-property-group>
        <url-pattern>*.jsp</url-pattern>
        <el-ignored>true</el-ignored>
    </jsp-property-group>
</jsp-config>

2. EL 表达式注入漏洞原理

2.1 漏洞示例代码分析

@RequestMapping("/el")
@ResponseBody
public String ElTest(@RequestParam(required = false) String expression) {
    if (expression == null) {
        return "Send request with \"expression\" parameter!";
    }
    try {
        ExpressionFactoryImpl factory = new ExpressionFactoryImpl();
        StandardELContext context = new StandardELContext(factory);
        ValueExpression valueExpression = factory.createValueExpression(context, expression, String.class);
        valueExpression.getValue(context);
    } catch (Exception e) {
        return Util.printStack(e);
    }
    return "OK!";
}

2.2 表达式解析与执行流程

  1. 表达式解析

    • ExpressionFactoryImpl#createValueExpression 创建 ValueExpression 对象
    • ExpressionBuilder#createValueExpression 构建表达式
    • build 方法将表达式拆分为 AST 节点
    • createNodeInternal 方法通过 AST 解析为各种节点类型
  2. 表达式执行

    • ValueExpressionImpl#getValue 获取表达式执行结果
    • 核心调用链:this.getNode().getValueAstValue#getValue
    • 处理流程:
      1. 获取第一个子节点的值
      2. 检查是否存在次子节点
      3. 如果存在 AstMethodParameters 节点,获取该节点
      4. 通过 resolver#invoke 方法结合 base/suffix/params 进行计算

3. SpEL (Spring Expression Language) 表达式注入

3.1 SpEL 与 EL 的区别

  1. 功能范围

    • SpEL 功能更强大,支持方法调用、类型操作等
    • EL 主要用于简化 JSP 中的数据访问
  2. 上下文

    • SpEL 可用于 Spring 应用的任何地方
    • EL 主要用于 JSP/JSF 等视图技术
  3. 语法

    • SpEL 使用 #{...} 语法
    • EL 使用 ${...} 语法

3.2 SpEL 注入漏洞示例

@RequestMapping("/spel")
@ResponseBody
public String SpELTest(@RequestParam String input) {
    ExpressionParser parser = new SpelExpressionParser();
    StandardEvaluationContext context = new StandardEvaluationContext();
    Expression exp = parser.parseExpression(input);
    return exp.getValue(context, String.class);
}

3.3 SpEL 安全防护

  1. 使用 SimpleEvaluationContext 替代 StandardEvaluationContext

    EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
    
  2. 禁用危险功能

    SpelParserConfiguration config = new SpelParserConfiguration(
        SpelCompilerMode.OFF, 
        this.getClass().getClassLoader()
    );
    ExpressionParser parser = new SpelExpressionParser(config);
    

4. 表达式注入攻击场景

4.1 常见攻击载荷

EL 注入示例

${''.getClass().forName('java.lang.Runtime').getMethod('exec',''.getClass()).invoke(''.getClass().forName('java.lang.Runtime').getMethod('getRuntime').invoke(null),'calc')}

SpEL 注入示例

T(java.lang.Runtime).getRuntime().exec('calc')

4.2 漏洞利用条件

  1. 用户输入直接作为表达式被解析
  2. 使用 StandardEvaluationContext(SpEL)
  3. 未对表达式内容进行过滤或限制

5. 防御措施

5.1 通用防御策略

  1. 避免直接解析用户输入

    • 不要将不可信输入直接作为表达式解析
  2. 使用安全上下文

    • SpEL:优先使用 SimpleEvaluationContext
    • EL:禁用或限制功能
  3. 输入过滤

    • 白名单验证表达式内容
    • 过滤危险字符和方法调用

5.2 代码示例

安全的 SpEL 使用

SpelExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
String safeExpression = "safeProperty"; // 来自可信源
Object value = parser.parseExpression(safeExpression).getValue(context);

安全的 EL 使用

// 禁用危险功能
ExpressionFactory factory = ExpressionFactory.newInstance();
ELContext context = new StandardELContext(factory);
// 限制可访问的对象和方法
context.addELResolver(new MySafeELResolver());

6. 总结

Java EL 和 SpEL 表达式注入漏洞源于对不可信输入的过度信任和不当解析。理解其工作原理和攻击方式对于构建安全的 Java Web 应用至关重要。开发者应:

  1. 充分认识表达式语言的潜在风险
  2. 采用最小权限原则配置表达式解析环境
  3. 实施严格的输入验证和过滤机制
  4. 定期进行安全审计和代码审查

通过合理的安全措施,可以在享受表达式语言便利性的同时,有效防范相关安全风险。

Java EL 和 SpEL 表达式注入漏洞深度解析 1. EL (Expression Language) 基础 1.1 EL 背景与特点 EL (Expression Language) 最初是为了简化 JSP 开发而设计的,主要特点包括: 可直接访问 JSP 内置对象 (page, request, session, application) 提供丰富的运算符(关系、逻辑、算术等) 支持扩展函数(可调用 Java 类的静态方法) 简化数据访问方式 1.2 EL 基本语法 EL 表达式语法格式: ${expression} 数据存取方式: 点号表示法: ${user.name} 方括号表示法: ${user["name"]} 特殊情况必须使用方括号: 属性名称包含特殊字符(如 . 、 - 等非字母数字字符) 需要动态取值时 1.3 EL 隐式对象 | 对象 | 等效 Java 代码 | 描述 | |------|---------------|------| | pageContext | PageContext | JSP 页面上下文 | | param | request.getParameter() | 获取单个请求参数 | | paramValues | request.getParameterValues() | 获取请求参数数组 | | header | request.getHeader() | 获取单个请求头 | | headerValues | request.getHeaders() | 获取请求头数组 | | cookie | request.getCookies() | 获取 cookie | 1.4 禁用 EL 表达式 单个 JSP 文件禁用 : 全局禁用 (web.xml 配置): 2. EL 表达式注入漏洞原理 2.1 漏洞示例代码分析 2.2 表达式解析与执行流程 表达式解析 : ExpressionFactoryImpl#createValueExpression 创建 ValueExpression 对象 ExpressionBuilder#createValueExpression 构建表达式 build 方法将表达式拆分为 AST 节点 createNodeInternal 方法通过 AST 解析为各种节点类型 表达式执行 : ValueExpressionImpl#getValue 获取表达式执行结果 核心调用链: this.getNode().getValue → AstValue#getValue 处理流程: 获取第一个子节点的值 检查是否存在次子节点 如果存在 AstMethodParameters 节点,获取该节点 通过 resolver#invoke 方法结合 base / suffix / params 进行计算 3. SpEL (Spring Expression Language) 表达式注入 3.1 SpEL 与 EL 的区别 功能范围 : SpEL 功能更强大,支持方法调用、类型操作等 EL 主要用于简化 JSP 中的数据访问 上下文 : SpEL 可用于 Spring 应用的任何地方 EL 主要用于 JSP/JSF 等视图技术 语法 : SpEL 使用 #{...} 语法 EL 使用 ${...} 语法 3.2 SpEL 注入漏洞示例 3.3 SpEL 安全防护 使用 SimpleEvaluationContext 替代 StandardEvaluationContext : 禁用危险功能 : 4. 表达式注入攻击场景 4.1 常见攻击载荷 EL 注入示例 : SpEL 注入示例 : 4.2 漏洞利用条件 用户输入直接作为表达式被解析 使用 StandardEvaluationContext (SpEL) 未对表达式内容进行过滤或限制 5. 防御措施 5.1 通用防御策略 避免直接解析用户输入 : 不要将不可信输入直接作为表达式解析 使用安全上下文 : SpEL:优先使用 SimpleEvaluationContext EL:禁用或限制功能 输入过滤 : 白名单验证表达式内容 过滤危险字符和方法调用 5.2 代码示例 安全的 SpEL 使用 : 安全的 EL 使用 : 6. 总结 Java EL 和 SpEL 表达式注入漏洞源于对不可信输入的过度信任和不当解析。理解其工作原理和攻击方式对于构建安全的 Java Web 应用至关重要。开发者应: 充分认识表达式语言的潜在风险 采用最小权限原则配置表达式解析环境 实施严格的输入验证和过滤机制 定期进行安全审计和代码审查 通过合理的安全措施,可以在享受表达式语言便利性的同时,有效防范相关安全风险。