这是 why 的第 103 篇原创
你好呀,我是 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/wik…
能够看到申请参数外面的确有工夫戳(timeStamp)和随机字符串(nonceStr),且人家还专门加粗了:
参加签名的参数为:appId、timeStamp、nonceStr、package、signType,参数辨别大小写。
那么是怎么签名的呢?
官网也是给了详尽的阐明的:
https://pay.weixin.qq.com/wik…
首先就是依照字典序,对所有须要参加签名的、非空的字段进行排序。并应用 URL 键值对的格局(即 key1=value1&key2=value2…)拼接成字符串 stringA。
而后在 stringA 最初拼接上 key(商户密钥)失去 stringSignTemp 字符串,并对 stringSignTemp 进行 MD5 运算,再将失去的字符串所有字符转换为大写,失去 sign 值 signValue。
官网给了一个理论的案例,如下:
再说一次:微信领取的接口外面尽管有工夫戳加随机串,然而目标不是为了避免重放攻打的。写在这里只是让大家对于加签这个过程有一个具体的认知。
别整茬了。
那么它在接口外面退出随机串的目标是什么呢?
官网本人都说了:
微信领取 API 接口协议中蕴含字段 nonce_str,次要保障签名不可预测。咱们举荐生成随机数算法如下:调用随机数函数生成,将失去的值转换为字符串。
阿里 API 网关
看完微信领取,再看看阿里的 API 网关是怎么避免重放攻打的。
https://help.aliyun.com/knowl…
阿里的 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. 从接管到的申请中读取客户端签名,比照服务器端签名和客户端签名的一致性。
而具体的签名算法其实和微信领取,大同小异,次要也是对于参加签名的字段依照字典序排序。
个中差别就不进行比照阐明了,有趣味的敌人能够本人看一下。
最初说一句
好了,看到了这里点个赞吧,周更很累的,须要一点正反馈。
满腹经纶,难免会有纰漏,如果你发现了谬误的中央,能够在留言区提出来,我对其加以批改。
感谢您的浏览,我保持原创,非常欢送并感谢您的关注。