你好呀,我是why。
如图,重放攻击,这题我真的在口试的时候碰着过,两次。
印象比较深的是第一次碰着这个口试题的时候,也是第一次听到“重放攻击”这个词的时候,一脸蒙蔽,于是我就连蒙带猜的,朝着接口幂等性的方向去答了。

结果就凉了。
要回答怎么防止重放攻击,那么我们得知道啥是重放攻击。
学术上的阐明是这样的:
重放攻击(英语:replay attack,或称为回放攻击)是一种恶意或敲诈的重复或延迟有效数据的网络攻击形式。 这可以由发起者或由拦截数据并重新传输数据的对手来实行,这可能是通过IP数据包更换进行的欺骗攻击的一部分。 这是“中间人攻击”的一个较低级别版本。 这种攻击的另一种描述是: “从不同高下文将重播到安全协议的预期(或原始和预期)高下文,从而欺骗其他参与者,致使他们误以为已经成功完成了协议运行。”
举个大略的例子:
我们程序员昼夜操劳的,在推拿店里面办个卡,偶尔去洗个脚放松一下不过分吧。
有一天,我去洗脚的时候对着店员说:给我安排一个 168 价位的,要小伙子啊,按着比较带劲儿,我的卡号是 88888888。
然后我在前台签上了自己的名字,店员就安排了一个精壮的小伙子给我推拿。
没想到我们的对话被其他人听到了,于是他也给店员说:给我安排一个 168 价位的,要小伙子啊,按着比较带劲儿,我的卡号是 88888888。
还模拟了我的署名,在前台具名。
把之前的、正常的要求再次发送,这便是重放攻击。
有的朋友就会说了:我的接口是加签的,该当没问题吧?
你加签咋了?
我没有动你的报文,以是你也可以正常验签呀。
我不仅抄你报文里面的正常字段,报文里面的署名我也抄全乎了。
以是,吸收方接到报文之后能正常验签。
没有任何毛病。
有的朋友还会说了:我的接口是有加密的,该当没问题吧?
看来还是不懂重放攻击的基本事理。
你加密咋了?
反正我截取到了你的报文,虽然你报文加密了,我看起来是一段乱码,但是我也不须要知道你报文的详细内容呀,直接重发就完事了。
还是前面的例子。
假设我去洗脚的时候对着店员说:天王盖地虎。
被阁下的人听到了,他根本就不知道“天王盖地虎”是啥。
但是他看到了我说了这句话之后,就被安排了一个 168 元的技能做事。
于是他也对店员说:天王盖地虎。
也能被安排。
以是,别人根本就不须要知道你报文的详细含义。
只要我再次发给你,你进行解密操作,创造能解密。
能解密解释暗号对上了。
以是,虽然报文是加密、加签传输的,对付防止要求重放,并没有什么卵用。
加密加签来,说办理方案之前,我们先明确两个观点:加密和加签。
字面意思不阐明了,大家都知道,说说目的。
加密的目的:为了担保传输信息的隐私性,不被别人看到传输的详细内容,只能让吸收方看到精确的信息。
加签的目的:吸收方验证信息是否是合法的发送方发送的,确认信息是否被其他人修改过。
不管是加密还是加签,都涉及到公私钥。
记住了:公钥加密、私钥加签。
大略的说一下事理。
发送方有这样三样东西:自己的私钥、自己的公钥、吸收方的公钥。
吸收方有这样三样东西:自己的私钥、自己的公钥、发送方的公钥。
中间人有这样两样东西:吸收方的公钥、发送方的公钥。
为什么是公钥加密呢?
来个反证法嘛。
假设发送方用自己的私钥加密。然后被中间人拦截到了,由于他有发送方的公钥,那么中间人就可以用公钥对进行解密,获取明文报文,这样达不到加密的目的。
以是,精确的操作该当是用吸收方的公钥加密,这样就算被中间人拦截到了,他也没有吸收方的私钥呀,解不了密,看不到明文。
为什么是私钥加签呢?
同样,反证法。
假设发送方,用吸收方的公钥加签。如果被中间人拦截到了,巧了,我也有吸收方的公钥。咔一下,直接把一改,然后也拿着吸收方的公钥加签,发过去了。
这样的加签是没故意义的。
因此,要用自己的私钥加签,就算被拦截,中间人没有私钥,修正报文之后,搞不了署名,也就没啥卵用。
前面说了,对付重放攻击,截取到的内容是不是加密都无所谓。由于我根本不须要你们在说什么,我只须要把拦截下来的要求一遍遍的重发就行了。
以是,主要的是加签和验签。
如果你能修正报文,并且重新加签,那就不叫重放攻击了,那就叫做中间人攻击了。
实在重放攻击也是“中间人攻击”的一个较低级别版本。
啥是中间人攻击呢?
我去洗脚的时候对着店员说:给我安排一个 168 价位的,要小伙子啊,按着比较带劲儿,我的卡号是 88888888。
对话被偷听到了,中间人对店员说:给我安排一个 1999 价位的,要小姑凉啊,推拿手腕好一点的,我的卡号是 88888888。
修改报文,这是中间人攻击。
本文紧张聚焦于重放攻击的办理方案。
经由前面的剖析,我们知道要办理重放攻击,便是想着怎么在参与署名的字段里面搞事情。
能想到这里,就比较好回答这个问题了。
如果是从数据加密角度回答这个问题的同学,可以回去等关照了。
其余,说到加密了,大家都会想到 HTTPS 数据加密。
以是,当口试官问你:HTTPS数据加密是否可以防止重放攻击?
答:否,加密可以有效防止明文数据被监听,但是却防止不了重放攻击。
接下来,我们看看办理方案。
办理方案加韶光戳
首先,常见的办理方案便是在要求报文里面加上韶光戳,并参与加签。
当吸收方收到报文,经由验签之后。
首先第一个事儿便是拿着要求中的韶光戳字段和本地韶光做个比拟。
如果韶光偏差在指定时间,比如 60 秒内,那么认为这个要求是合理的,程序可以连续处理。
为什么要有一个韶光容错范围,能理解吧?
由于报文的传输、解密、验签是须要韶光,不能假设我这一秒发出去,下一秒做事端就收到了。
以是,得有韶光容错范围。
但是这个容错范围又带来了其余一个问题。
不能完备避免重放攻击。
至少韶光容错范围内,比如 60 秒,重发过来的要求,做事端认为是有效的。
那么怎么办呢?
加随机串
换个思路,我们在要求报文里面加个随机串,然后让它参与加签。
接管方收到报文,验签之后,把随机串拿出来,来判断一下这个随机串是否已经处理过了。比如判断一下是否存在于 Redis 里面。
当要求再次重放过来的时候,一看:嚯,好家伙,这个随机串已经被用过了呀,不处理了。
在这个情形下,随机串就得担保唯一性了,还得历史全局唯一。
由于你指不定哪天就收到一个几天前的被重放过来的要求。
确实是办理了要求重放的问题,但是弊端也很明显:历史全局唯一。
我还得存储下来,而且存储的数据量还会越来越大,是不是有点麻烦了?
确实麻烦了。
这个思想就和用全局唯一流水号去担保接口幂等性很像了。
以是,我第一次碰着这个口试题的时候,我朝着接口幂等的角度去回答了,也不能说回答的不对。
只能说回答的不是口试官想要的标准答案。
那么什么是口试官想要听到的回答呢?
韶光戳+随机串
韶光戳的问题是有一定的韶光容错窗口,这个韶光窗口内的重放攻击是防不住的。
随机串的问题是要担保历史全局唯一,保存随机串成了一个麻烦的事情。
那么当我们把这两个方案揉在一起的时候,神奇的事情就发生了:
我只须要担保韶光窗口内的天生的随机串不重复就行。
而且假设韶光窗口为 60 秒,我们用 Redis 来记录涌现过的随机串,那么这个串在后台的超时时间设置为 60 秒就行。
一样平常来说这个韶光窗口都不会太长了,我对接过这么多各种各样的渠道,见过最长的也就 5 分钟。
担保 5 分钟内天生的两个随机串不重复,这个需求比担保实现一个历史全局唯一的流水号随意马虎实现多了吧?
其余,最关键的一句话一定要说:韶光戳和随机串得参与到加签逻辑中去。
这个很好理解吧?
接管方看报文是否被修改,看的便是署名是否能匹配上。
而署名的结果是和参与署名的字段的值有直接关系的。
假如你韶光戳和随机串不参与加签,那么任意修正韶光戳或者随机串,都不会引起署名的变革,那不白忙活一场吗?
中间人咔一下拦截到要求,创造有韶光戳和随机串,正准备放弃的时候,想着去世马当做活马医,把随机串一改,又扔给吸收方了。
结果收到精确的相应了。
我假如这个中间人,我都会笑出来声来:写这个代码的程序员也太可爱了吧?
微信支付实在说到韶光戳加随机串的时候,我就想起了微信支付。
刚刚入行的时候,可是被这个微信支付搞的服帖服帖的。
但是须要解释的是,虽然它的接口文档里面也有韶光戳加随机串,但是目的不是为了防止重放攻击的。
写出来呢只是为了让对付加签这个东西不太熟习的朋友有一个详细的认知。
来,我们看一下微信支付的接口文档:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
可以看到要求参数里面确实有韶光戳(timeStamp)和随机字符串(nonceStr),且人家还专门加粗了:
参与署名的参数为:appId、timeStamp、nonceStr、package、signType,参数区分大小写。
那么是怎么署名的呢?
官方也是给了详尽的解释的:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
首先便是按照字典序,对所有须要参与署名的、非空的字段进行排序。并利用 URL 键值对的格式(即key1=value1&key2=value2…)拼接成字符串 stringA。
然后在 stringA 末了拼接上 key(商户密钥) 得到 stringSignTemp 字符串,并对 stringSignTemp 进行 MD5 运算,再将得到的字符串所有字符转换为大写,得到 sign 值 signValue。
官方给了一个实际的案例,如下:
再说一次:微信支付的接口里面虽然有韶光戳加随机串,但是目的不是为了防止重放攻击的。写在这里只是让大家对付加签这个过程有一个详细的认知。
别整茬了。
那么它在接口里面加入随机串的目的是什么呢?
官方自己都说了:
微信支付API接口协议中包含字段nonce_str,紧张担保署名不可预测。我们推举天生随机数算法如下:调用随机数函数天生,将得到的值转换为字符串。
阿里API网关看完微信支付,再看看阿里的 API 网关是怎么防止重放攻击的。
https://help.aliyun.com/knowledge_detail/50041.html
阿里的 API 网关,便是在 HEADER 里面加了两个参数:X-Ca-Timestamp、X-Ca-Nonce。
这个办理方案便是我们前面说的韶光戳加随机串。
接着看看它的署名天生过程。
首先是客户端天生署名,三步:
1.从原始要求中提取关键数据,得到一个用来署名的字符串
2.利用加密算法加APP Secret对关键数据署名串进行加密处理,得到署名
3.将署名所干系的所有头加入到原始HTTP要求中,得到终极HTTP要求
一图胜千言:
然后是做事端验证署名,四步:
1.从吸收到的要求中提取关键数据,得到一个用来署名的字符串
2.从吸收到的要求中读取APP Key,通过APP Key查询到对应的APP Secret
3.利用加密算法和APP Secret对关键数据署名串进行加密处理,得到署名
4.从吸收到的要求中读取客户端署名,比拟做事器端署名和客户端署名的同等性。
而详细的署名算法实在和微信支付,大同小异,紧张也是对付参与署名的字段按照字典序排序。
个中差异就不进行比拟解释了,有兴趣的朋友可以自己看一下。
末了说一句好了,看到了这里点个赞吧,周更很累的,须要一点正反馈。
才疏学浅,难免会有疏忽,如果你创造了缺点的地方,可以在留言区提出来,我对其加以修正。
感谢您的阅读,我坚持原创,十分欢迎并感谢您的关注。