曾今做API网关项目的时候,遇到过重放攻击,这是一种比较常见的攻击。那什么是重放攻击?
百度百科
重放攻击(Replay Attacks)又称重播攻击、回放攻击,是指攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程,破坏认证的正确性。重放攻击可以由发起者,也可以由拦截并重发该数据的敌方进行。攻击者利用网络监听或者其他方式盗取认证凭据,之后再把它重新发给认证服务器。重放攻击在任何网络通过程中都可能发生,是计算机世界黑客常用的攻击方式之一。
针对重放攻击,主要有以下几种防护方式
百度百科
(1)加随机数。该方法优点是认证双方不需要时间同步,双方记住使用过的随机数,如发现报文中有以前使用过的随机数,就认为是重放攻击。缺点是需要额外保存使用过的随机数,若记录的时间段较长,则保存和查询的开销较大。
(2)加时间戳。该方法优点是不用额外保存其他信息。缺点是认证双方需要准确的时间同步,同步越好,受攻击的可能性就越小。但当系统很庞大,跨越的区域较广时,要做到精确的时间同步并不是很容易。
(3)加流水号。就是双方在报文中添加一个逐步递增的整数,只要接收到一个不连续的流水号报文(太大或太小),就认定有重放威胁。该方法优点是不需要时间同步,保存的信息量比随机数方式小。缺点是一旦攻击者对报文解密成功,就可以获得流水号,从而每次将流水号递增欺骗认证端。
以上几个防护方式单独使用,都有不同的缺点,那到底怎么样才能更好的防备重放攻击呢?时间戳+随机数的方式。
时间戳的处理
时间戳的应用存在以下几个问题,需要进行优化
客户端与服务端时间不同步,时间可能差距很大。所以客户端与服务端之间的时间差限制不能太小,比如10分钟
仅仅使用时间戳来防护,阈值范围内的请求,无法做到放重放
我们采用***相对时间***来解决时间戳无法解决客户端服务端时间不同步的问题。
假设客户端时间为t1,服务端时间为t2。 客户端首次启动的时候,预设相对时间T=0。
客户端采用t1+T作为时间戳发起请求
如果时间差过大,服务端拒绝请求,会返回当前服务端时间t2(可以使用标准时间)给客户端
客户端更新相对时间T = t2-t1,并将时间缓存到本地以便下一次启动时可以使用。重新回到第二步
通过相对时间,不管服务端时间异常,还是客户端时间异常,抑或跨时区本身时间不一致,都可以保证发起请求时,传的时间戳不会相差过大,一般都不会超过1秒,如果超过1秒,对客户端体现已经产生影响,需要优化请求本身
随机数的处理
随机数的生成方式有多种,无论何种方式,服务端都需要识别该随机数是不是出现过。因为请求本身就需要防参数篡改,签名也基本上保证了随机性,我们直接使用了签名来作为随机数。
最大的问题是如何保存随机数。保存时间越久,对服务端的压力就越大。对时间戳进行优化后,保险起见,我们假设时间差超过10秒会拒绝请求,那么我们只要保存10秒的随机数就可以做好防重放攻击。
不同的访问量可以选择不同的方案。
如果访问量较低,比如不超过1000QPS,那么只要保存不超过10000个随机数,单机服务本地cache就可以解决。分布式服务可以通过redis等缓存也可以解决。
如果访问量很大,比如100万QPS,那么需要保存1000万个随机数,怎么处理?使用MD5签名获得的随机数长32位,也就是32个字节,1000万随机数,需要3.2亿个字节,大概需要300多MB的空间,这是个不小的空间。如果1000万QPS呢,就要3G以上。我们可以使用***布隆过滤器***(布隆过滤器就不做介绍,可以自行查询,布隆过滤器主要需要注意的是误算率)来解决这个问题,redis刚好提供了bitmap来实现。
利用布隆过滤器保存签名,需要将16进制转换为数字,使用BigInteger来防止越界。为了降低误算率,可以通过签名计算出多个不同的数字,我们使用的签名与签名倒序计算出的2个数字。 这两个数字就可以作为布隆过滤器的2位。
privateLong[] getOffsets(String sign) {
//使用BigInteger来保存大数字
BigInteger positiveNum =newBigInteger(sign,16);
BigInteger reverseNum =newBigInteger(newStringBuilder(sign).reverse().toString(),16);
Long[] offsets =newLong[2];
offsets[0] = positiveNum.mod(BigInteger.valueOf(Integer.MAX_VALUE /2)).longValue();
offsets[1] = reverseNum.mod(BigInteger.valueOf(Integer.MAX_VALUE /2)).longValue();
returnoffsets;
}
使用更大的bitmap也可以降低误算率,比如采用一个10亿位的bitmap来保存1000万的随机数。但是对于一个10亿位的bitmap操作存在以下问题:
1.初始化过慢
2.读写数据慢
有以下几点改进措施:
- 请求中带有timestamp和随机数,可以根据不同的时间将随机数路由到不同的bitmap里,如10ms,50ms,可以解决一个bitmap过大导致的初始化慢的问题,误算率也可以更低
- 可以组合别的参数来路由到不同的bitmap
- 对不同的api做区分,不需要防重放攻击的接口,可以不用做计算,可以大大减少接口处理数
本文介绍了重放攻击的概念及其常见防护方式,包括加随机数、加时间戳和加流水号。重点讨论了时间戳+随机数的组合策略,通过相对时间解决客户端与服务端时间不同步问题,以及如何使用布隆过滤器在高访问量场景下有效存储和检查随机数,降低内存占用和误算率。
716

被折叠的 条评论
为什么被折叠?



