API接口参数防篡改与重放攻击

前言

参数篡改是一种常见的黑客攻击方式,尤其是对商城等与金钱相关联的API接口,参数篡改会直接造成经济损失。重放攻击是另一种常见的黑客攻击形式,当用户的请求被嗅探或截获时,如果接口未做防重放攻击,劫持者可直接使用截获的参数对接口进行重放攻击,导致系统异常。本文所有接口以无状态接口为例,使用spring boot。项目开源地址

项目环境

工具:idea,postman
java环境:java8
项目源代码:开源地址

正文

针对以上情况我们一般有哪些解决方案呢。

方案:

1.针对参数篡改问题

参数篡改主要问题主要是由于客户端所传参数属于明文(未使用有效的https或者https被劫持),劫持者可直接修改参数。解决这个问题方法和简单:
1)客户端在提交参数前对参数进行MD5摘要。
2)客户端直接使用公钥对参数进行加密,传递加密后字符串。
这里我们主要讲解方法1,方法2与方法1类似。

步骤:

1.这里我们使用aop注入的方式去实现以上两个功能。
项目中引入aop支持

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

2.创建注解 TamperProofParams

/**
 * Des: 
 * ClassName: TamperProofParams
 * Author: createsboy
 * Date: 2019/7/19
 * Time: 16:38
 */
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TamperProofParams {

}

3.aop实现

/**
 * Des:
 * ClassName: TamperProofParamsAop 
 * Author: createsboy
 * Date: 2019/7/19
 * Time: 16:33
 */
@Aspect
@Component
public class TamperProofParamsAop {
    private static final Logger LOGGER = LoggerFactory.getLogger(TamperProofParamsAop.class);
    /**
     * 缓存
     */
    @Autowired
    private CacheUtil cacheUtil;

    /**
     * 此处的切点是注解的方式
     * 也可以用包名匹配的方式达到同样的效果
     * '@Pointcut("execution(* com.createsboy.api.*.controller.*(..))")'
     */
    @Pointcut("@annotation(com.createsboy.api.annotation.TamperProofParams)")
    public void tamperProofParams() {
    }


    /**
     * 参数防篡改校验
     *
     * @param joinPoint
     */
    @Before("tamperProofParams()")
    public void doBeforeAdvice(JoinPoint joinPoint) {
        LOGGER.info("参数防篡改校验.....");
        //获取请求参数
        Request request = null;
        for (Object item : joinPoint.getArgs()) {
            if (item instanceof Request) {
                request = (Request) item;
                break;
            }
        }
        //如果请求参数为空
        if (request == null) {
            throw new RuntimeException("请求异常");
        } else {
        	//比对请求时间与当前时间差异
            long diff = Math.abs(System.currentTimeMillis() - request.getTimestamp());
            //如果大于3分钟直接返回请求异常
            if (diff > 180000) {
                throw new RuntimeException("请求异常,超时");
            }
            //拼接时间和参数字符串
            String str = request.getTimestamp() + JSON.toJSONString(request.getParams());
            //进行MD5摘要
            String md5 = MD5Util.encrytor(str);
            if (md5.equals(request.getCheck())) {
                LOGGER.info("参数未被篡改");
            } else {
                throw new RuntimeException("请求异常参数被篡改");
            }
        }
    }
}

1).首先获取Request对象

/**
 * Des:
 * ClassName: Request
 * Author: biqiang2017@163.com
 * Date: 2019/8/21
 * Time: 18:24
 */
public class Request<T>{
    /**
     * 参数
     */
    @Valid
    private T params;

    /**
     * 校验
     */
    @NotBlank(message = "参数有误")
    private String check;

    /**
     * 版本
     */
    @NotNull(message = "参数有误")
    private String version;

    /**
     * 时间戳
     */
    @NotNull(message = "参数有误")
    private Long timestamp;

    public T getParams() {
        return params;
    }

    public void setParams(T params) {
        this.params = params;
    }

    public String getCheck() {
        return check;
    }

    public void setCheck(String check) {
        this.check = check == null ? null : check.trim();
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version == null ? null : version.trim();
    }

    public Long getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(Long timestamp) {
        this.timestamp = timestamp;
    }
}

2).获取Request对象中timestamp提交时间,校验是否超时。
3).使用json对参数进行序列化,然后进行MD5摘要。(该项目json工具使用的FastJson,FastJson在对对象序列化时,如果引用对象没有对参数进行排序设置【如有不了解可直接百度FastJson排序,后期我会更新关于FastJson排序问题】,FastJson会按照默认字母表排序,这里一定要注意,客户端提交参数进行摘要时也需要顺序相同,可参考微信开放接口)
4).比对MD5摘要是否相同。

2.针对API接口重放攻击

重放攻击主要发生在接口参数被劫持,然后模拟劫持参数对开放API接口进行攻击,劫持者一般不会修改劫持参数,因此防参数篡改无法预防重放攻击。其实解决中方攻击也很简单,只需将参数MD5放入Cache缓存,对每次提交的参数MD5进行Cache查找,如果存在表明此次提交为无效提交属于重放攻击,但是这里有一个问题需要注意就是对参数进行MD5摘要时需要加入客户端的timestamp时间,这样才不会错误的把两次参数相同的提交视为重放攻击(timestamp时间不一样,一般采用当前毫秒值)。但是这里有一个问题就是随着请求的增加,我们的Cache消耗会越来越大,因此这里我们一般会对摘要设置一个过期时间,这里才有一天清理,保留一天的时间

if (md5.equals(request.getCheck())) {
  	 if (!cacheUtil.setNXValue(md5, 86400, "repeat")) {//预防重放攻击
        throw new RuntimeException("请求异常,参数被篡改");
      }
      LOGGER.info("参数未被篡改");
} else {
     throw new RuntimeException("请求异常,校验码异常");
 }
测试

使用工具Postman
headers参数:
Content-Type:application/json;charset=UTF-8
在这里插入图片描述
参数模板

{
    "params":{
        "name":"Test",
        "code":"VB147258"
    },
    "check":"",
    "version":"v1.0",
    "timestamp":毫秒值
}

在这里插入图片描述
使用base包先MD5Util工具可生成请求参数
在这里插入图片描述

拷贝cmd中第一个和第二个参数到timestamp和check中,点击send查看结果
在这里插入图片描述

第二次点击send发现返回请求异常,重放攻击
在这里插入图片描述
关于API防参数篡改与重放攻击的总结到此结束。关于博客中的任何问题欢迎大家留言讨论,一起进步。
敬我们敬爱的java。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值