APP之防重放攻击

什么是重放攻击?

我们在设计接口的时候,最怕一个接口被用户截取用于重放攻击。重放攻击是什么呢?就是把你的请求原封不动地再发送一次,两次...n次,一般正常的请求都会通过验证进入到正常逻辑中,如果这个正常逻辑是插入数据库操作,那么一旦插入数据库的语句写的不好,就有可能出现多条重复的数据。一旦是比较慢的查询操作,就可能导致数据库堵住等情况,如果是付款接口,或者购买接口就会造成损失,因此需要采用防重放的机制来做请求验证。

 

HTTPS数据加密是否可以防止重放攻击?

否,加密可以有效防止明文数据被监听,但是却防止不了重放攻击。

 

如何防重放攻击?答案:客户端在请求参数加上 时间戳 + 随机数

  • 时间戳(timestamp)。以服务端当前时间为准,服务端要求客户端发过来的时间戳,必须是最近60秒内(假设值)的。这样,这个请求即使被截取了,你也只能在60s内进行重放攻击。
  • 随机数(nonce)。但是,攻击者还有60s的攻击时间呢!所以我们需要在客户端请求中再加上一个随机数(中间黑客不可能自己修改随机数,因为有参数签名的校验呢)服务端会对一分钟内请求的随机数进行检查,如果有两个相同的,基本可以判定为重放攻击。因为正常情况下,在短时间内(比如60s)连续生成两个相同nonce的情况几乎为0。
    PS:我们假设这个数足够随机,比如 md5(timestamp+rand(0, 1000)); 也可以使用UUID。

 

服务端如何校验

服务端第一次在接收到这个nonce的时候做下面行为:

  1. 
去redis中查找是否有key为nonce:{nonce}的string

  2. 如果没有,则创建这个key,把这个key失效的时间和验证timestamp失效的时间一致,比如是60s。

  3. 如果有,说明这个key在60s内已经被使用了,那么这个请求就可以判断为重放请求。

校验示例:

请求:http://a.com/?uid=123&timestamp=1480556543&nonce=43f34f33&sign=80b886d71449cb33355d017893720666
这个请求中,

  • uid是我们真正需要传递的有实际意义的参数,
  • timestamp、nonce、sign都是为了签名和防重放使用。
  • timestamp是发送接口的时间,nonce是随机串,sign是对uid、timestamp、nonce的签名。签名的方法可以是md5({秘要}key1=val1&key2=val2&key3=val3...)

服务端接到这个请求:

  1. 先验证sign签名是否合理,证明请求参数没有被中途篡改

  2. 再验证timestamp是否过期,证明请求是在最近60s被发出的

  3. 最后验证nonce是否已经有了,证明这个请求不是60s内的重放请求

 

web层面也可以采用在页面中加入token方式、手机验证码、滑动验证码等方式来防止攻击。

 

防御手段

防止重放攻击的方法是使用不重数

1.加随机数

  • 优点是认证双方不需要时间同步,双方记住使用过的随机数,如发现报文中有以前使用过的随机数,就认为是重放攻击。// ------“双方” 意思是客户端、服务端都需要做防重放。但现状是一般只对客户端要求。
  • 缺点是需要额外保存使用过的随机数,若记录的时间段较长,则保存和查询的开销较大。

2. 加时间戳

  • 方法优点是不用额外保存其他信息。
  • 缺点是认证双方需要准确的时间同步,同步越好,受攻击的可能性就越小。但当系统很庞大,跨越的区域较广时,要做到精确的时间同步并不是很容易。
     
            private readonly string TimeStamp = ConfigurationManager.AppSettings["TimeStamp"];//配置时间戳
            [HttpPost]
            public ActionResult TestApi()
            {
                    string RequestTime = Request["rtime"]; //请求时间经过RSA签名
                    try
                    {
                        //请求时间RSA解密后加上时间戳的时间即该请求的有效时间
                        DateTime Requestdt = DateTime.Parse(RSACryptoProvider.Decrypt(RequestTime, RSA_Keys.Private)).AddMinutes(int.Parse(TimeStamp)); 
                        DateTime Newdt = DateTime.Now; //服务器接收请求的当前时间
                        //if 请求的有效时间 < 现在服务器接受请求的时间 即该请求失效
                        if (Requestdt < Newdt)
                        {
                            return Json(new { success = false, message = "该请求已经失效" });
                        }
                        else
                        {
    		    //进行其他操作
    		    }
                    }
                    catch (Exception ex)
                    {
                        return Json(new { success = false, message = "请求参数不和要求" });
                    }
            } 

3. 加流水号
就是双方在报文中添加一个逐步递增的整数,只要接收到一个不连续的流水号报文(太大或太小),就认定有重放威胁。

  • 该方法优点是不需要时间同步,保存的信息量比随机数方式小。
  • 缺点是一旦攻击者对报文解密成功,就可以获得流水号,从而每次将流水号递增欺骗认证端。

 

在实际中,常将方法(1)和方法(2)组合使用,这样就只需保存某个很短时间段内的所有随机数,而且时间戳的同步也不需要太精确。
对付重放攻击除了使用本以上方法外,还可以使用挑战一应答机制和一次性口令机制,而且似乎后面两种方法在实际中使用得更广泛。

 

 

 

 

 

本文参考:https://segmentfault.com/a/1190000014821815 有删改

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值