怎么应对API 成批分配问题?

1、问题分析

API 成批分配利用可能导致特权升级、数据篡改、绕过安全机制。
对于POST请求,如果请求时body里存在后台不需要的字段,但是响应成功

2、问题重现

正常传参是 {"page":1,"pageSize":10,"userPhone":""} ,现在给一个多几个属性的参数也能响应成功,这是不对的:{"page":1,"pageSize":10,"userPhone":"","isadmin":true,"issso":true,"role":"admin"},这说明测试结果存在漏洞,因为测试响应成功(返回 200 OK),这表明多传参了但应用程序/API 访问成功。
测试多传参数属性的请求和响应:

POST	/ngbsp-api/user-act/admin/discuss/black/list	HTTP/1.1
Host:	10.110.120.101:89
Accept:	application/json,	text/plain,	*/*
X-CSRF-TOKEN:	a6a5d13a-74e5-4507-bbc9-4fc0ccf94d7a
X-Requested-With:	XMLHttpRequest
User-Agent:	Mozilla/5.0	(Windows	NT	10.0;	Win64;	x64)	AppleWebKit/537.36	(KHTML,	like	Gecko)	Chrome/126.0.0.0	Safari/537.36Content-Type:	application/json;
Origin:	http://10.110.120.101:89
Referer:	http://10.110.120.101:89/ngbsp/
Accept-Language:	en-US
Connection:	keep-alive
Content-Length:	82
Cookie:	LCUID=LzWATXrrydZKuIW4n18FFtf1mAoEUwqHy2AO0zGqMk9wz7l3tiOqZXpz/seb3o4IOyVQA4jS93S2k5OpJyRS8W62Y5Rxx+fTURpA+eQzD5g=;NGBSPSID=OGEwMjlmOTAtMDEzZi00NGY2LTk0OGQtOTM3YTg1NjYyYThj;	LCSSID=OGEwMjlmOTAtMDEzZi00NGY2LTk0OGQtOTM3YTg1NjYyYThj{
	"page":	1,
	"pageSize":	10,
	"userPhone":	"",
	"isadmin":	true,
	"issso":	true,
	"role":	"admin"
}
HTTP/1.1	200	OK
Server:	nginx/1.21.6
Date:	Wed,	24	Jul	2024	09:15:53	GMT
Content-Type:	application/json
Transfer-Encoding:	chunked
Connection:	keep-alive
Cache-Control:	no-cache,	no-store,	max-age=0,	must-revalidate
Expires:	0
Pragma:	no-cache
Referrer-Policy:	no-referrer
X-Content-Type-Options:	nosniff
X-Frame-Options:	DENY
X-Xss-Protection:	1	;	mode=block
{
	"code":	"P00000",
	"msg":	"",
	"data":	{
	 	"total":	4,
	 	"records":	[
			{
	 	 	 	"userPhone":	"17863200022",
	 	 	 	"nickName":	"178****0022",
	 	 	 	"userName":	null,
	 	 	 	"joinTime":	"2021-06-16	14:14:53"
	 	 	} ,
			{
	 	 	 	"userPhone":	"13710000001",
	 	 	 	"nickName":	"137****0001",
	 	 	 	"userName":	null,
	 	 	 	"joinTime":	"2021-06-12	14:55:27"
	 	 	} ,
			{
	 	 	 	"userPhone":	"15564156415",
	 	 	 	"nickName":	"155****6415",
	 	 	 	"userName":	null,
	 	 	 	"joinTime":	"2021-06-10	14:22:19"
	 	 	} ,
			{
	 	 	 	"userPhone":	"13620000001",
	 	 	 	"nickName":	"136****0001",
	 	 	 	"userName":	null,
	 	 	 	"joinTime":	"2021-06-08	10:23:21"
			}
	 	] ,
	 	"current":	1,
	 	"size":	4,
	 	"pages":	1,
	 	"listSize":	0
	 }
}

2、修复建议

  • (1)body使用VO或者DTO接收
  • (2)对body进行校验,增加反序列化配置方案
方案一,通过yml配置
spring:
  jackson:
    deserialization:
      fail-on-unknown-properties: true

方案二,自定义配置类

@Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.removeIf(MappingJackson2HttpMessageConverter.class::isInstance);
        converters.add(0, mappingJackson2HttpMessageConverter());
    }

    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"));
        return new MappingJackson2HttpMessageConverter(objectMapper);
    }
}
  • (3)统一异常捕获或者返回处增加非200状态码
@ExceptionHandler(value = HttpMessageNotReadableException.class)
    public ResponseEntity<ResponseDTO> methodHttpMessageNotReadableExceptionHandler(
            HttpServletRequest request, HttpMessageNotReadableException e) {
        logger.error("请求body中存在实体类中不存在的参数");
        logger.error("请求地址:{}", request.getServletPath());
        ResponseDTO responseDTO = ResponseDTO.error(ErrorCodeEnum.S_REQ_PARAM_ERROR, "");
        return new ResponseEntity<>(responseDTO, HttpStatus.INTERNAL_SERVER_ERROR);
    }

4、具体操作方法

1、建DTO,前端传什么参数,DTO里的属性就是什么,可以将页面的属性提取出来继承。

public class AdminDiscussBlackDTO {

    private String userPhone;

    private Integer page;

    private Integer limit;
    // get、set方法
    }

2、接口参数接收修改
之前的出现API成批分配的java代码接口:
之前是用@RequestBody Map param接收的参数,现在是@RequestBody AdminDiscussBlackDTO discussBlackDTO 接收参数。

 @PostMapping("/list")
    public ResponseDTO list(@Valid @RequestBody AdminDiscussBlackDTO discussBlackDTO) {
        Map<String, Object> param = BeanUtil.beanToMap(discussBlackDTO);
        PageBean<DiscussBlackEntity> pageBean = discussBlackService.list(param);
        return WebUtils.createSuccessResponse(pageBean);
    }

3、在application.yml里配置反序列化
在这里插入图片描述
4、异常处理

/**
 * 异常处理器
 */
@RestControllerAdvice
public class RRExceptionHandler {

  /**
     * 捕获反序列化异常HttpMessageNotReadableException,增加500状态码返回
     *
     * @param request 请求
     * @param e       异常对象
     * @return 响应
     */
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    public ResponseEntity<ResponseDTO> methodHttpMessageNotReadableExceptionHandler(
            HttpServletRequest request, HttpMessageNotReadableException e) {
        logger.error("请求body中存在实体类中不存在的参数");
        logger.error("请求地址:{}", request.getServletPath());
        logger.error("用户id:{}", MsySecurityContextHolder.getUser().getUserId());
        ResponseDTO responseDTO = WebUtils.createFailureResponse(ErrorCodeEnum.S_REQ_PARAM_ERROR, "参数不合法");
        return new ResponseEntity<>(responseDTO, HttpStatus.INTERNAL_SERVER_ERROR);

   	    // 判断是否是整数溢出问题,其他的问题的异常处理
        if (e.getRootCause() instanceof InputCoercionException || e.getRootCause() instanceof InvalidFormatException) {
            logger.error("类型转换错误");
            logger.error("请求地址:{}", request.getServletPath());
            ResponseDTO responseDTO = WebUtils.createFailureResponse(ErrorCodeEnum.S_REQ_PARAM_ERROR, "非法的请求参数");
            return new ResponseEntity<>(responseDTO, HttpStatus.OK);
        }
    }
}

5、修改完的测试

1、多传个其他的请求参数,报错。
在这里插入图片描述
2、参数名不对,也报错
在这里插入图片描述
3、少传个参数没事,比如说userPhone不传,不报错。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农小C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值