说说 API 的防重放机制
说说API的防重放机制
我们在设计接口的时候,最怕一个接口被用户截取用于重放攻击。重放攻击是什么呢?就是把你的请求原封不动地再发送一次,两次...n次,一般正常的请求都会通过验证进入到正常逻辑中,如果这个正常逻辑是插入数据库操作,那么一旦插入数据库的语句写的不好,就有可能出现多条重复的数据。一旦是比较慢的查询操作,就可能导致数据库堵住等情况。
这里就有一种防重放的机制来做请求验证。
timestamp+nonce
我们常用的防止重放的机制是使用timestamp和nonce来做的重放机制。
timestamp用来表示请求的当前时间戳,这个时间戳当然要和服务器时间戳进行校正过的。我们预期正常请求带的timestamp参数会是不同的(预期是正常的人每秒至多只会做一个操作)。每个请求带的时间戳不能和当前时间超过一定规定的时间。比如60s。这样,这个请求即使被截取了,你也只能在60s内进行重放攻击。过期失效。
但是这样也是不够的,还有给攻击者60s的时间。所以我们就需要使用一个nonce,随机数。
nonce是由客户端根据足够随机的情况生成的,比如 md5(timestamp+rand(0, 1000)); 它就有一个要求,正常情况下,在短时间内(比如60s)连续生成两个相同nonce的情况几乎为0。
服务端
服务端第一次在接收到这个nonce的时候做下面行为:
1 去redis中查找是否有key为nonce:{nonce}的string
2 如果没有,则创建这个key,把这个key失效的时间和验证timestamp失效的时间一致,比如是60s。
3 如果有,说明这个key在60s内已经被使用了,那么这个请求就可以判断为重放请求。
示例
那么比如,下面这个请求:
http://a.com?uid=123×tamp=1480556...
这个请求中国的uid是我们真正需要传递的有意义的参数
timestamp,nonce,sign都是为了签名和防重放使用。
timestamp是发送接口的时间,nonce是随机串,sign是对uid,timestamp,nonce(对于一些rest风格的api,我建议也把url放入sign签名)。签名的方法可以是md5({秘要}key1=val1&key2=val2&key3=val3...)
服务端接到这个请求:
1 先验证sign签名是否合理,证明请求参数没有被中途篡改
2 再验证timestamp是否过期,证明请求是在最近60s被发出的
3 最后验证nonce是否已经有了,证明这个请求不是60s内的重放请求
本作品采用《CC 协议》,转载必须注明作者和本文链接
目测不解决问题,重放攻击者自己随机生成nonce就可以了
@leo 签名有秘药
@leo 签名的规则不知道,改nonce,第一步sign就验证不通过了
密钥是在登录的时候返回给前端?
url上的sign是怎么生成的...
@william 楼主说的是给后端用的api,不是给前端用的,一开始我也没搞明白,所以才会有上面的疑问
@leo 原来是这样。也就是密钥是不存在交换的过程的,可以认为密钥是安全的存放在请求方那里的
我们现在的签名和校验也基本是这样做的。
我们团队 api v1 也是这样处理的,写了 php node java 的package,https://github.com/LingxiTeam/api-authenti...
很赞啊,刚才看到微信公众平台也是这么做的,:smile:
不是说 https 为分片数据编号,可以防止重放攻击吗?
目前用的jwt-auth,一直不知道怎样防止重放攻击。 总不可能给每个token设置1分钟的过期时间吧,这样感觉有点违背jwt的设计了
嗯,思路很清晰!:+1:
@RryLee star+1
我很想探讨一个问题:
如果是前后端分离的项目,我怎么来用时间戳校验过期呢?
因为每个用户的客户端生成的时间是可能是不一样的啊。
可能和服务器端不在同一时区。这样服务器端怎么来校验过期呢?
@lemayi
使用时间戳校验,因为时间戳是无时区的。
确实是适用服务端,对于客户端貌似并不行,客户端我们该怎么预防重放攻击?