java安全开发之spring boot Thymeleaf模板注入
字数 1941 2025-08-15 21:31:56
Spring Boot Thymeleaf模板注入安全开发指南
1. Thymeleaf简介
Thymeleaf是一个用于Web和独立环境的现代服务器端Java模板引擎,类似于Python Web开发中的Jinja模板引擎。它是Spring Boot官方推荐的模板引擎。
2. Spring Boot基础知识
2.1 Spring MVC与Spring Boot关系
- Spring Boot是Spring MVC的简化版本,在Spring MVC基础上实现了自动配置
- 通过
DispatcherServlet前端控制器拦截请求 - 请求处理流程:
DispatcherServlet拦截请求(如/users)- Servlet决定使用哪个handler处理
- Spring检测哪个控制器匹配请求路径
- 从
@RequestMapping中查找需要的信息 - 执行匹配的Controller方法
- 返回处理结果(对象列表)
- 根据需求返回Json或Xml格式数据
2.2 常用注解
@Controller:处理HTTP请求@RestController:@Controller的衍生注解@RequestMapping:路由请求,可设置各种操作方法@GetMapping:GET方法的路由@PostMapping:POST方法的路由@PutMapping:PUT方法的路由@DeleteMapping:DELETE方法的路由@PathVariable:处理请求URL路径中的参数(如/user/{id})@RequestParam:处理问号后面的参数@RequestBody:请求参数以json格式提交@ResponseBody:返回json格式
3. 漏洞环境配置
示例项目:https://github.com/veracode-research/spring-view-manipulation/
核心漏洞代码:
@GetMapping("/path")
public String path(@RequestParam String lang) {
return lang; //template path is tainted
}
代码含义:用户请求URL为/path,参数名称为lang,服务器通过Thymeleaf模板查找相关模板文件。例如请求/path?lang=en,服务器会查找resources/templates/en.html并返回。
4. 模板注入分析
4.1 请求处理流程
DispatcherServlet在org.springframework.web.servlet.ModelAndView方法中开始处理请求- 在
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle中:- 通过
invokeForRequest函数调用相关controller - 将返回值作为待查找的模板文件名
- 通过
- 在
org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler#handleReturnValue中:- 将controller返回值作为视图名称
- 在
org.springframework.web.servlet.DispatcherServlet#processDispatchResult中:- 调用Thymeleaf模板引擎的表达式解析
- 将视图名称解析为模板名称并加载
4.2 漏洞点
Thymeleaf允许在模板名称中使用表达式,导致当用户输入被直接作为模板名称时,可能执行任意代码。
5. 不安全的代码示例
5.1 直接返回用户输入
@GetMapping("/path")
public String path(@RequestParam String lang) {
return lang; //template path is tainted
}
攻击Payload:
GET /path?lang=__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22whoami%22).getInputStream()).next()%7d__::.x HTTP/1.1
Host: 127.0.0.1:8090
Connection: close
5.2 无返回值的Controller
根据Spring Boot定义,如果controller无返回值,则以GetMapping的路由为视图名称。
@GetMapping("/doc/{document}")
public void getDocument(@PathVariable String document) {
log.info("Retrieving " + document);
}
攻击Payload:
GET /doc/__${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x
6. 修复方案
6.1 设置@ResponseBody注解
@GetMapping("/safe/path")
@ResponseBody
public String safePath(@RequestParam String lang) {
return lang; //不再调用模板解析
}
6.2 设置redirect重定向
@GetMapping("/safe/redirect")
public String redirect(@RequestParam String url) {
return "redirect:" + url; //调用RedirectView而非ThymeleafView
}
注意:此方案可能导致CWE-601漏洞(开放重定向),需验证重定向URL
6.3 使用HttpServletResponse
@GetMapping("/safe/doc/{document}")
public void getDocument(@PathVariable String document, HttpServletResponse response) {
log.info("Retrieving " + document);
//Spring认为已处理HTTP Response,不会进行视图名称解析
}
7. 最佳实践
- 永远不要直接将用户输入作为模板名称
- 对需要动态选择模板的场景,使用白名单验证用户输入
- 优先使用
@ResponseBody注解返回数据 - 对于视图返回,明确指定模板路径而非使用用户输入
- 定期进行安全审计,检查潜在的模板注入漏洞