网络请求中使用随机数避免重放攻击的原理是什么?

2021-11-23 01:38:57 +08:00
 Richard14

注意到所有的这些云平台提供的 api ,调用的过程中都需要生成一个随机数,然后根据随机数生成签名。调用百度翻译、腾讯翻译 api 的时候都是如此。据说是为了避免被重放攻击。那么它的原理是什么呢?

第一次能请求成功的话,签名就是合法的,如果这时候第二次进行了完全相同的请求,服务器怎么知道该请求属于重放呢?

疑问一:是默认不同时间生成的随机数完全不同吗?有没有可能刚好两次临近请求生成了相同的随机数?比如一秒内刚好两次生成 9527 这个数。

疑问二:后端如何拦截?难道要维护一个队列,每个 ID 最近三分钟内请求的随机数都有哪些?类似这种的?如果这么搞的话成本不会爆炸吗?

2406 次点击
所在节点    问与答
25 条回复
binux
2021-11-23 01:57:59 +08:00
> 调用的过程中都需要生成一个随机数
哪里说要生成一个随机数了?
Rocketer
2021-11-23 03:14:08 +08:00
虽然没看过文档,但感觉用随机数不如直接用时间戳,超时忽略即可。

至于说维护一个 request id (随机数)的库就更不太可能了,分布式系统同步这个数据的成本可不低。
Chad0000
2021-11-23 03:39:58 +08:00
就是避免攻击者使用同一个请求。一般不会使用随机数而使用时间戳,比如允许有 5 分钟时间差,这样复制原请求的攻击只能限定在一定时间内。严格的话可以缓存这个时间戳(比如根据日志分析某个 Client 可能被攻击,标记此 Client 需要检查)
binux
2021-11-23 03:48:40 +08:00
看了下百度是用随机数的 salt ,腾讯是 5 分钟有效的 timestamp 。
但是百度签名用的是 md5 ,加 salt 是为了防止选择明文攻击吧。
Chad0000
2021-11-23 03:59:25 +08:00
@binux #4 跟明文攻击没关系,验证算法比如是:MD5 ( Reqeust Data + KEY + Salt ),Salt 是时间戳,Key 是自己的密码。这种方式中间人只能重复这个请求,直到这个请求不被认可,比如重复使用 Salt 或者 Salt 过期(如果是时间戳)。
binux
2021-11-23 04:34:18 +08:00
@Chad0000 首先百度没有说 Salt 是时间,它也不会当作时间处理,其次如 LZ 所说,它存储 salt 太浪费资源了,或者 salt 有随机生成重复导致请求失败的可能。
所以应该不是为了避免重放攻击。
Veneris
2021-11-23 08:43:21 +08:00
我之前实现过这个逻辑,https://github.com/viticis/API-Signed

其实就是 签名 = 算法 ( appId + 接口 + 时间戳 + 随机数 )
四个参数只要有一个有一点点变动,签名就完全是不一样的了。

那么可以认为,同一个 appId (即同一个第三方用户),在同一毫秒内,调用同一个接口,使用了同一个随机数的情况下,大概率为重放攻击,只需要判断 appId -> 签名 在缓存中有无对应关系即可。
Richard14
2021-11-23 08:57:16 +08:00
@Veneris 所以还是要保存一个 appid 到签名的对应关系?是保存 appid 的最近请求的所有签名,然后每次新请求都挨个匹配?感觉成本会非常爆炸。否则的话难道只保存最近一次的请求?那么如果攻击者通过某种途径截取到了身份认证的封包,等一分钟以后重新发送,攻击不就成功了吗
Veneris
2021-11-23 09:05:22 +08:00
@Richard14 #8 可以把这几个参数拼接成 key ,签名为 value ,使用 redis 做判断 key 存不存在就可以了,我们当时的业务来说,没什么太大开销。第二个问题的话,除了 签名校验,重放攻击校验,还有一个时间校验,要求 调用发起方 与 服务器 时差在 10s 内,两个目的,一个就是防止你说的这个情况,一个是 redis 里的 key 可以设置 10s 过期,防止大量堆积。

步骤来说,首先判断时间,时差过大直接拒绝,时差在误差以内,通过拼接的 key 是否在 redis 中存在来判断是否重放攻击,是的话拒绝,否的话,再校验签名,在转发到对应的业务层。
LeeReamond
2021-11-23 09:07:57 +08:00
@Veneris 懂了,所以你的意思是防止重放攻击的手段是 redis 上搞个队列,里面存全服所有的( appId + 接口 + 时间戳 + 随机数)请求,比如维护一个三分钟队列之类的,这样队列也不会特别吃内存,搜索开销也不大,这样?
Veneris
2021-11-23 09:11:09 +08:00
@LeeReamond #10 以我们当时的业务来说,没这么多第三方调用,加上一般这种时间戳会要求 10s ,不会说客户端和服务器时差 3 分钟还允许调用,所以 redis 其实压力不大,但我也不知道大厂的这种业务是什么架构,只是说实现了一样的功能
LeeReamond
2021-11-23 09:26:56 +08:00
@Veneris 感觉问题不大,稍微切一切分一分,感觉大厂也能接受这种架构,挺合理的
sujin190
2021-11-23 09:42:39 +08:00
既然你已经想到不能避免重放,那就别怀疑了,没有人可以超越物理限制的,事实上这个是用来权限验证的,你看签名需要密钥吧,而这个不会放在参数里,一般没有防止重放攻击的作用,想防重放可以加入时间参数,签名校验成功后检查超过一定时间拒掉就是了
Chad0000
2021-11-23 09:49:34 +08:00
@sujin190 #13 其实也能在指定场景下完全避免重放。我这边就是时间戳,同时限定后来的请求时间戳要比之前的大,这个数据 Redis 缓存即可。在他们请求不太频繁的情况下随便调用不影响,太频繁了调用方就需要控制调用顺序啦 - 一举两用。这个时间戳其实也就是计数器,用时间戳的好处是绝大部分情况下(非高频)无需对方全局管理这个值。
Greenm
2021-11-23 10:56:51 +08:00
想不通的话参考一下验证码和 TOTP ,加上几分钟内有效就是为了防止重放
clf
2021-11-23 11:03:48 +08:00
可能是用来算 QPS 的。
documentzhangx66
2021-11-23 11:18:15 +08:00
首先,请求加 ID ,并且每次请求要更换 ID ,是为了实现客户端因为一些问题,比如网络故障,导致用户重复点击业务办理按钮之类的事情,导致重复办理。

一般情况下,直接用一个自增 ID 就行了。
BeijingBaby
2021-11-23 11:54:40 +08:00
背后是分布式系统。
加个随机数,是为了请求过程中重试,防止重复处理消息。
Zy143L
2021-11-23 11:57:50 +08:00
最经典的就是时间戳
icyalala
2021-11-23 11:59:18 +08:00

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/817263

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX