java学习基地

微信扫一扫 分享朋友圈

已有 1743 人浏览分享

电商系统中API接口如何防止参数被篡改和重放攻击?

[复制链接]
1743 2
本帖最初由 进修派 于 2020-12-3 21:47 编纂


阐明:今朝一切当钡统架构皆是接纳前后端别离当钡统架构,那末便不成能制止的需求效劳对中供给API,那末怎样包管对中的API的宁静呢?


死陈电商中API接心避免参数窜改战重放进犯

目次


1. 甚么是API参数窜改?

阐明:API参数窜改便是歹意仁炸过抓包的方法获得到恳求的接心的参数,经由过程修正相干的参数,到达棍骗效劳器的目标,经常使用的避免窜改的方法是用署名和减稀的方法。


2. 甚么是API重收进犯?

阐明:API重放进犯: 便是把之前盗听到的数据一成不变的从头收收给领受圆.


3,经常使用的处理的计划

经常使用的其他营业场景另有:


  • 收收短疑接心
  • 付出接心

基于timestamp战nonce的计划

微疑付出的接心便是如许做的

timestamp的感化

每次HTTP恳求,皆需求减上timestamp参数,然后把timestamp战其他参数一同停止数字署名。HTTP恳求从收回抵达效劳器普通皆没有会超越60s,以是效劳器支到HTTP恳求以后,起首判定工夫戳参数取当前工夫比拟较,能否超越了60s,假如超越了则以为长短法的恳求。


普通状况下,从抓包重放恳求耗时近近超越了60s,以是此时恳求中的timestamp参数曾经生效了,假如修正timestamp参数为当前的工夫戳,则signature参数洞喀的数字署名便会生效,由于没有明白署名秘钥,出诱法天生新的数字署名。


但这类方法的破绽也是不言而喻的,假如正在60s以后停止重放进犯,那便出法子了,以是这类方法不克不及包管恳求仅一次有用


nonce的感化

nonce的意义是仅一次有用的随机字符串,请求每次恳求时,该参数要包管差别。我们将每次恳求的nonce参数存储迪苹个⊥汞开”中,每匆薛理HTTP恳求时,起首判定该恳求的nonce参数能否正在该⊥汞开”中,假如存正在则以为长短法恳求。


nonce参数正在初次恳求时,曾经被存储到了效劳器上的⊥汞开”中,再次收收恳求会被辨认并回绝。


nonce参数做为数字署名的一部门,是没法窜改的,由于没有明白署名秘钥,出诱法天生新的数字署名。


这类方法也有很年夜的成绩,那便是存储nonce参数的⊥汞开”会愈来愈年夜。


nonce的一次性能够处理timestamp参数60s(避免重放进犯)的成绩,timestamp能够处理nonce参数⊥汞开”愈来愈年夜的成绩。


啡ホ改、防重放进犯 阻拦器(用到了redis)
  1. public class SignAuthInterceptor implements HandlerInterceptor {
  2.     private RedisTemplate<String, String> redisTemplate;
  3.     private String key;
  4.     public SignAuthInterceptor(RedisTemplate<String, String> redisTemplate, String key) {
  5.         this.redisTemplate = redisTemplate;
  6.         this.key = key;
  7.     }
  8.     @Override
  9.     public boolean preHandle(HttpServletRequest request,
  10.                              HttpServletResponse response, Object handler) throws Exception {
  11.         // 获得工夫戳
  12.         String timestamp = request.getHeader("timestamp");
  13.         // 获得随机字符串
  14.         String nonceStr = request.getHeader("nonceStr");
  15.         // 获得署名
  16.         String signature = request.getHeader("signature");
  17.         // 判定工夫能否年夜于xx秒(避免重放进犯)
  18.         long NONCE_STR_TIMEOUT_SECONDS = 60L;
  19.         if (StrUtil.isEmpty(timestamp) || DateUtil.between(DateUtil.date(Long.parseLong(timestamp) * 1000), DateUtil.date(), DateUnit.SECOND) > NONCE_STR_TIMEOUT_SECONDS) {
  20.             throw new BusinessException("invalid  timestamp");
  21.         }
  22.         // 判定该映雩的nonceStr参数能否曾经正在redis中(避免短工夫内的重放进犯)
  23.         Boolean haveNonceStr = redisTemplate.hasKey(nonceStr);
  24.         if (StrUtil.isEmpty(nonceStr) || Objects.isNull(haveNonceStr) || haveNonceStr) {
  25.             throw new BusinessException("invalid nonceStr");
  26.         }
  27.         // 对恳求头彩俏数停止署名
  28.         if (StrUtil.isEmpty(signature) || !Objects.equals(signature, this.signature(timestamp, nonceStr, request))) {
  29.             throw new BusinessException("invalid signature");
  30.         }
  31.         // 将本次映雩恳求的nonceStr参数存到redis中设置xx秒后主动删除
  32.         redisTemplate.opsForValue().set(nonceStr, nonceStr, NONCE_STR_TIMEOUT_SECONDS, TimeUnit.SECONDS);
  33.         return true;
  34.     }
  35.     private String signature(String timestamp, String nonceStr, HttpServletRequest request) throws UnsupportedEncodingException {
  36.         Map<String, Object> params = new HashMap<>(16);
  37.         Enumeration<String> enumeration = request.getParameterNames();
  38.         if (enumeration.hasMoreElements()) {
  39.             String name = enumeration.nextElement();
  40.             String value = request.getParameter(name);
  41.             params.put(name, URLEncoder.encode(value, CommonConstants.UTF_8));
  42.         }
  43.         String qs = String.format("%s×tamp=%s&nonceStr=%s&key=%s", this.sortQueryParamString(params), timestamp, nonceStr, key);
  44.         log.info("qs:{}", qs);
  45.         String sign = SecureUtil.md5(qs).toLowerCase();
  46.         log.info("sign:{}", sign);
  47.         return sign;
  48.     }
  49.     /**
  50.      * 根据字母挨次停止降序排序
  51.      *
  52.      * @param params 恳求参数 。留意恳求参数中不克不及包罗key
  53.      * @return 排序后成果
  54.      */
  55.     private String sortQueryParamString(Map<String, Object> params) {
  56.         List<String> listKeys = Lists.newArrayList(params.keySet());
  57.         Collections.sort(listKeys);
  58.         StrBuilder content = StrBuilder.create();
  59.         for (String param : listKeys) {
  60.             content.append(param).append("=").append(params.get(param).toString()).append("&");
  61.         }
  62.         if (content.length() > 0) {
  63.             return content.subString(0, content.length() - 1);
  64.         }
  65.         return content.toString();
  66.     }
  67. }
赶钙代码

总结:做互联网使用,不管是死陈小法式仍是APP,宁静永久皆是第一名,宁静做好了,其他的才气做得更好,固然,疑息宁静是一个永世的话题,并不是经由过程本文就可以够道得分明的

期望本文能够给各人一面考虑取倡议。





举报 使用道具

回复

评论 2

青春舞曲  vip终身会员  发表于 2020-12-22 19:06:40 | 显示全部楼层
撸过

举报 使用道具

回复
shanmaojp  vip终身会员  发表于 2020-12-22 20:13:17 | 显示全部楼层
我也来顶一下..

举报 使用道具

回复
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

0

关注

0

粉丝

138

主题
精彩推荐
热门资讯
网友晒图
图文推荐

Archiver|手机版|java学习基地 |网站地图

GMT+8, 2021-9-17 04:57 , Processed in 0.478485 second(s), 26 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.