首页 > 其他分享 >面试官:你讲下接口防重放如何处理?

面试官:你讲下接口防重放如何处理?

时间:2024-06-10 15:23:51浏览次数:11  
标签:面试官 return String timestamp 接口 token 防重 请求

前言

我们的API接口都是提供给第三方服务/客户端调用,所有请求地址以及请求参数都是暴露给用户的。

我们每次请求一个HTTP请求,用户都可以通过F12,或者抓包工具fd看到请求的URL链接,然后copy出来。这样是非常不安全的,有人可能会恶意的刷我们的接口,那这时该怎么办呢?防重放攻击就出来了。

什么是防重放攻击

我们以掘金文章点赞为例。当我点赞之后,H5会发送一个请求给到掘金后端服务器,我可以通过f12看到完整的请求参数,包括url,param等等,然后我可以通过copy把这个请求给copy出来,那么我就可以做到一个放重放攻击了。

具体如下。我们可以看到,服务端返回的是重复点赞,也就是掘金并没有做我们所谓世俗意义上的放重放攻击。掘金通过查询数据库(推测item_id是唯一索引值),来判断是否已经点赞然后返回前端逻辑。

那么什么是我们理解的放重放呢

简单来说就是,前端和客户端约定一个算法(比如md5),通过加密时间戳+传入字段。来起到防止重复请求的目的。
然后这个时间戳可以设定为30秒,60秒过期。

那么如果30秒,有人不断刷我们的接口怎么办。
我们还可以新加一个字段为nonceKey,30秒内随机不重复。这个字段存放在Redis,并且30秒过期。
如果下一次请求nonceKey还在redis,我就认为是重复请求,拒绝即可。

算法实现

  1. 首先定义一个全局拦截器
@Component
public class TokenInterceptor implements HandlerInterceptor {

	@Autowired
	private StringRedisTemplate redisService;

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		String timestamp = request.getParameter("timestamp");
		String token = request.getParameter("token");
		if (timestamp == null || token == null) {
			return false;
		}

		TreeMap<String, String> map = new TreeMap<>();
		Enumeration<String> parameterNames = request.getParameterNames();
		while (parameterNames.hasMoreElements()) {
			String str = parameterNames.nextElement();
			if (StringUtils.equals(str, "token")) {
				continue;
			}
			map.put(str, request.getParameter(str));
		}

		return SecretUtils.extractSecret(redisService, timestamp, token, map);
	}
}
  1. 定义具体的算法实现
public class SecretUtils {

	private static final long NONCE_DURATION = 60 * 1000L;
	private static final String SALT = "salt"; // 注意这块加盐

	public static boolean extractSecret(StringRedisTemplate redisService, String timestamp, String token, TreeMap<String, String> map) {
		if (StringUtils.isEmpty(timestamp) || StringUtils.isEmpty(token)) {
			return false;
		}
		long ts = NumberUtils.toLong(timestamp, 0);
		long now = System.currentTimeMillis();
		if ((now - ts) > SecretUtils.NONCE_DURATION || ts > now) {
			return false;
		}

		StringBuilder sb = new StringBuilder();
		map.put(SALT, SALT);
		for (Map.Entry<String, String> entry : map.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue();
			if (sb.length() > 0) {
				sb.append("&");
			}
			sb.append(key).append("=").append(value);
		}

		String targetToken = DigestUtils.md5DigestAsHex(sb.toString().getBytes());
		if (!token.equals(targetToken)) {
			return false;
		}

		String s = redisService.opsForValue().get(timestamp);
		if (StringUtils.isNotEmpty(s)) {
			return false;
		} else {
			redisService.opsForValue().set(timestamp, timestamp, NONCE_DURATION, TimeUnit.MILLISECONDS);
		}

		return true;
	}

}

前端会通过我们事先约定好的算法以及方式,将字符串从小到大进行排序 + timestamp,然后md5进行加密生成token传给后端。后端根据算法+方式来校验token是否有效。

如果其中有人修改了参数,那么token就会校验失败,直接拒绝即可。如果没修改参数,timestamp如果大于60s,则认为是防重放攻击,直接拒绝,如果小于30s,则将nonceKey加入到redis里面,这里nonceKey用的是timestamp字段,如果不存在则第一次请求,如果存在,则直接拒绝即可。

通过这么简单的一个算法,就可以实现防重放攻击了。

Q&A

Q:客户端和服务端生成的时间戳不一致怎么办

A:客户端和服务端生成的是时间戳,不是具体的时间,时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数

Q:HTTPS数据加密是否可以防止重放攻击

A:不可以。https是在传输过程中保证了加密,也就是说如果中间人,获取到了请求,他是无法解开传输的内容的。

举个最简单的例子,上课和同学传纸条的时候,为了不让中间给递纸条的人看到或者修改,可以在纸条上写成只有双方能看明白密文,这样递纸条的过程就安全了,传纸条过程中的人就看不懂你的内容了。但是如果给你写纸条的人要搞事情,那就是加密解决不了的了。这时候就需要放重放来解决了。

Q:防重放攻击是否有用,属于脱裤子放屁

A:个人感觉有一点点吧。比如防重放攻击的算法+加密方式其实大多数用的都是这些,其实攻击人很容易就能猜到token生成的方式,比如timestamp + 从小到大排序。因此我们加入了salt来混淆视听,这个salt需要前端、客户端安全的存储,不能让用户知道,比如js混淆等等。但其实通过抓包,js分析还是很容易能拿到的。但无形中增加了攻击人的成本,比如网易云登录的js加密类似。

Q:做了防重放,支付,点赞等是否不需要做幂等了

A:需要。最重要的幂等,一定要用数据库来实现,比如唯一索引。其他都不可相信。

最后

以我个人的理解。防重放用处不大,其他安全措施,比如非对称的RSA验签更加有效。就算用户拿到了请求的所有信息,你的接口也一定要做幂等的,尤其是像支付转账等高危操作,幂等才是最有用的防线。而且防重发生成token的算法,大家都这样搞,攻击者怎么可能不知道呢?这点我不太理解。

现在面试也比较考验面试官的水平,下篇我会讲下最近的一些面试体验和感受,欢迎大家点赞收藏。

标签:面试官,return,String,timestamp,接口,token,防重,请求
From: https://www.cnblogs.com/wenbochang/p/18240697

相关文章

  • 短信接口封装
    腾讯云短信封装发送短信#-*-coding:utf-8-*-fromtencentcloud.commonimportcredentialfromtencentcloud.common.exception.tencent_cloud_sdk_exceptionimportTencentCloudSDKExceptionfromtencentcloud.sms.v20210111importsms_client,modelsfromtencentclo......
  • 当接口出现404问题时,可能出现问题的原因如下
    当我们在进行网络应用开发或者使用API时,经常会遇到后端接口返回404(NotFound)错误的情况。这种错误通常意味着客户端请求了服务器上不存在的资源,可能由多种原因造成。下面将详细介绍这些原因,并给出相应的解决方法。1.资源路径错误一个常见的原因是客户端请求的资源路径不正确。这......
  • 天气Api接口
    接口请求格式如下:http://cdn.weather.hao.360.cn/sed_api_weather_info.php?app=360chrome&code=【地区编码】&_jsonp=【jsonp回调函数】其中的地区编码与中国天气网的地区编码是一样的。如果不设置这个参数,则默认显示本地的天气状况。简易的调用示例源码如下:(请自行进行......
  • Python爬虫:通过js逆向了解某音请求接口参数a_bogus加密过程
    1.前言需要提前说明以下,本篇文章讲述的内容仅供学习,切莫用于商业活动,如若被相关人员发现,本小编概不负责!切记。。本次分析的接口为:https://www.douyin.com/aweme/v1/web/discover/search/它的请求方式为:GET请求需要的参数有:请求参数中需要进行js逆向是:a_bogus必须需要的请......
  • (16)DAC接口--->(001)FPGA实现AD5601接口(一)
     (001)FPGA实现AD5601接口(一)1目录(a)FPGA简介(b)IC简介(c)Verilog简介(d)FPGA实现AD5601接口(一)(e)结束1FPGA简介(a)FPGA(FieldProgrammableGateArray)是在PAL(可编程阵列逻辑)、GAL(通用阵列逻辑)等可编程器件的基础上进一步发展的产物。它是作为专用集成电路(ASIC)领域中的一种半......
  • RGMII接口--->(011)FPGA实现RGMII接口(十一)
     (011)FPGA实现RGMII接口(十一)1目录(a)FPGA简介(b)IC简介(c)Verilog简介(d)FPGA实现RGMII接口(十一)(e)结束1FPGA简介(a)FPGA(FieldProgrammableGateArray)是在PAL(可编程阵列逻辑)、GAL(通用阵列逻辑)等可编程器件的基础上进一步发展的产物。它是作为专用集成电路(ASIC)领域中的一种......
  • 6.9找回机制接口安全&验证码token接口
    响应包responseburp截取拦截,改相应包;思路:此处应该若是修改密码,先抓到修改成功数据包(截取验证关键字),在替换为需要绕过的数据包,截取response数据包,修改验证成功关键字达到绕过效果;1.发送验证码2.验证3.重制密码1-3跳过2;短信轰炸实例接口调用发包;应用程序注册模块没用添加......
  • 关于类、继承、接口的复习(1)
    均使用这个层次结构:多态:一个对象变量可以指示多种实际类型动态绑定:一个对象变量在运行时能够自动选择适合的方法注:对象变量是一种“引用”,引用不同块对象的内存,“指示多种实际类型”就是一个对象变量可以在不同情况下引用了多种有继承关系的类型,规则是——对象变量在继承层次......
  • 通过接口拿到代理,返回代理列表(只有http,无https)
    defget_proxies_list(num):res=requests.get(url=f"http://api.89ip.cn/tqdl.html?api=1&num={num*2}&port=&address=&isp=").textip_list=re_method(res,"<br>(.*?)<br>")proxies_list=[]foriin......
  • 对象业务的重命名接口
    依据AWSS3,没有定义重命名对象的操作。本文有如下假定:对象存储服务基于文件语义实现。接口定义依据前述,业界主流对象存储服务比如AWSS3并未定义重命名对象的操作,而国内的各家公有云对象存储服务,提供了对象的重命名操作。国内的公有云对象存储服务,相关操作的文档的链接(排名......