FreeMarker入门到简要分析模版注入
字数 1626 2025-08-24 16:48:15

FreeMarker模板引擎从入门到安全分析

一、FreeMarker简介

FreeMarker是一款模板引擎,基于模板和要改变的数据生成输出文本(HTML网页、电子邮件、配置文件、源代码等)。它不是面向最终用户,而是一个Java类库,是程序员可以嵌入到开发产品中的组件。

主要特点:

  • 使用专用语言FreeMarker Template Language (FTL)
  • 专注于数据展示,业务逻辑在模板外处理
  • 语法类似JSP的EL表达式或Thymeleaf,使用${}或标签

二、环境搭建与基本配置

1. SpringBoot整合FreeMarker

依赖引入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

配置文件示例

server:
  port: 8081
spring:
  freemarker:
    # 配置项说明
    allow-request-override: false  # HttpServletRequest属性是否可覆盖model同名项
    request-context-attribute: req  # req访问request
    suffix: .html  # 模板后缀,默认为.ftl
    content-type: text/html;charset=utf-8  # 响应内容类型
    enabled: true  # 是否允许mvc使用freemarker
    cache: false  # 是否开启模板缓存
    template-loader-path: classpath:/templates/  # 模板加载路径
    charset: UTF-8  # 模板编码

三、基本语法与使用

1. Controller示例

@GetMapping
public String index(Model model){
    model.addAttribute("msg", "你好,老铁");
    model.addAttribute("flag", true);
    model.addAttribute("createData", new Date());
    return "index";
}

2. 模板文件(index.html)示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>hello,家人们</h1>
    <h1>msg:${msg}</h1>
    
    <!-- 布尔类型转换 -->
    <h1>f1:${flag?string}</h1>
    ${flag?string("yes","no")}
    
    <!-- 日期类型处理 -->
    ${createData?date}  <!-- 年月日 -->
    ${createData?time}  <!-- 时分秒 -->
    ${createData?datetime}  <!-- 年月日时分秒 -->
    ${createData?string("yyyy/MM/dd HH")}  <!-- 自定义格式 -->
    
    <!-- 字符串处理 -->
    <h5>字符串类型</h5>
    ${msg}
    ${msg?substring(0,1)}  <!-- 截取字符串 -->
    ${msg?uncap_first}  <!-- 首字母小写 -->
    ${msg?cap_first}  <!-- 首字母大写 -->
</body>
</html>

3. 常用语法说明

  • 变量输出${variable}
  • 布尔值转换
    • flag?string:转换为字符串
    • flag?string("yes","no"):三元运算形式
  • 日期处理
    • ?date:只显示年月日
    • ?time:只显示时分秒
    • ?datetime:显示完整日期时间
    • ?string("format"):自定义格式
  • 字符串处理
    • ?substring(start,end):字符串截取
    • ?uncap_first:首字母小写
    • ?cap_first:首字母大写

四、安全漏洞分析

1. XSS漏洞

当模板中直接输出用户可控内容时,可能导致XSS漏洞:

model.addAttribute("msg", "<script>alert(1)</script>");

模板中直接使用${msg}输出时,会解析script标签,导致XSS。

2. RCE漏洞(远程代码执行)

攻击Payload

<#assign value="freemarker.template.utility.Execute"?new()>${value("open -a Calculator")}

漏洞分析:

  1. assign指令

    • 语法:<#assign key=value>
    • 示例:<#assign value="freemarker.template.utility.Execute"?new()>
    • 作用:创建一个名为value的变量,其值为Execute类的新实例
  2. ?new()

    • FreeMarker从2.3.17版本开始,只支持实现了TemplateModel接口的类
    • Execute类实现了TemplateMethodModel接口(继承自TemplateModel
    • 因此可以通过?new()调用其构造函数
  3. 方法调用

    • ${value("open -a Calculator")}
    • 调用Execute实例的exec方法,传入命令参数
    • 参数实际上以List形式传递,取第一个元素作为命令执行

底层机制:

  • FreeMarker允许通过?new()实例化实现了TemplateModel接口的类
  • freemarker.template.utility.Execute类提供了命令执行功能
  • 攻击者可以构造任意命令实现RCE

3. 漏洞验证示例

测试类

package com.springboot.pojo;

public class Test {
    public Test() {
        System.out.println("123");
    }
    
    public void hello() {
        System.out.println("Hello");
    }
}

模板测试

<#assign value="com.springboot.pojo.Test"?new()>

成功调用Test类的构造函数,输出"123"

五、安全防护建议

  1. 输入过滤

    • 对所有用户输入进行严格的过滤和转义
    • 特别是对于要在模板中输出的内容
  2. FreeMarker配置

    • 使用最新版本的FreeMarker
    • 禁用危险的指令和功能
  3. 沙箱环境

    • 在可能的情况下,使用沙箱环境运行FreeMarker
    • 限制可访问的Java类和方法的范围
  4. 代码审计

    • 检查所有使用?new()的地方
    • 确保不会动态加载不受信任的类
  5. 输出编码

    • 对于所有动态内容输出,使用适当的编码函数
    • 例如:${userInput?html}进行HTML编码

六、深入分析资源

如需深入了解FreeMarker模板注入的详细流程,可参考:
https://blog.csdn.net/qq_38154820/article/details/127982704

该文章详细分析了FreeMarker模板注入的底层机制和调用流程,包括断点调试和参数传递过程。

FreeMarker模板引擎从入门到安全分析 一、FreeMarker简介 FreeMarker是一款模板引擎,基于模板和要改变的数据生成输出文本(HTML网页、电子邮件、配置文件、源代码等)。它不是面向最终用户,而是一个Java类库,是程序员可以嵌入到开发产品中的组件。 主要特点: 使用专用语言FreeMarker Template Language (FTL) 专注于数据展示,业务逻辑在模板外处理 语法类似JSP的EL表达式或Thymeleaf,使用 ${} 或标签 二、环境搭建与基本配置 1. SpringBoot整合FreeMarker 依赖引入 : 配置文件示例 : 三、基本语法与使用 1. Controller示例 2. 模板文件(index.html)示例 3. 常用语法说明 变量输出 : ${variable} 布尔值转换 : flag?string :转换为字符串 flag?string("yes","no") :三元运算形式 日期处理 : ?date :只显示年月日 ?time :只显示时分秒 ?datetime :显示完整日期时间 ?string("format") :自定义格式 字符串处理 : ?substring(start,end) :字符串截取 ?uncap_first :首字母小写 ?cap_first :首字母大写 四、安全漏洞分析 1. XSS漏洞 当模板中直接输出用户可控内容时,可能导致XSS漏洞: 模板中直接使用 ${msg} 输出时,会解析script标签,导致XSS。 2. RCE漏洞(远程代码执行) 攻击Payload : 漏洞分析: assign指令 : 语法: <#assign key=value> 示例: <#assign value="freemarker.template.utility.Execute"?new()> 作用:创建一个名为value的变量,其值为 Execute 类的新实例 ?new() : FreeMarker从2.3.17版本开始,只支持实现了 TemplateModel 接口的类 Execute 类实现了 TemplateMethodModel 接口(继承自 TemplateModel ) 因此可以通过 ?new() 调用其构造函数 方法调用 : ${value("open -a Calculator")} 调用 Execute 实例的 exec 方法,传入命令参数 参数实际上以List形式传递,取第一个元素作为命令执行 底层机制: FreeMarker允许通过 ?new() 实例化实现了 TemplateModel 接口的类 freemarker.template.utility.Execute 类提供了命令执行功能 攻击者可以构造任意命令实现RCE 3. 漏洞验证示例 测试类 : 模板测试 : 成功调用Test类的构造函数,输出"123" 五、安全防护建议 输入过滤 : 对所有用户输入进行严格的过滤和转义 特别是对于要在模板中输出的内容 FreeMarker配置 : 使用最新版本的FreeMarker 禁用危险的指令和功能 沙箱环境 : 在可能的情况下,使用沙箱环境运行FreeMarker 限制可访问的Java类和方法的范围 代码审计 : 检查所有使用 ?new() 的地方 确保不会动态加载不受信任的类 输出编码 : 对于所有动态内容输出,使用适当的编码函数 例如: ${userInput?html} 进行HTML编码 六、深入分析资源 如需深入了解FreeMarker模板注入的详细流程,可参考: https://blog.csdn.net/qq_ 38154820/article/details/127982704 该文章详细分析了FreeMarker模板注入的底层机制和调用流程,包括断点调试和参数传递过程。