API重放攻击(Replay Attacks)又称重播攻击、回放攻击。他的原理就是把以前窃听到的数据原封不动的从新发送给接收方。HTTPS并不能防止这种攻击,虽然传输的数据是通过加密的,窃听者没法获得数据的准肯定义,可是能够从请求的接收方地址分析出这些数据的做用。好比用户登陆请求时攻击者虽然没法窃听密码,可是却能够截取加密后的口令而后将其重放,从而利用这种方式进行有效的攻击。redis
所谓重放攻击就是攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程,重放攻击是计算机世界黑客经常使用的攻击方式之一。sql
一次HTTP请求,从请求方到接收方中间要通过不少个路由器和交换机,攻击者能够在中途截获请求的数据。假设在一个网上存款系统中,一条消息表示用户支取了一笔存款,攻击者彻底能够屡次发送这条消息而偷窃存款。数据库
重放是二次请求,若是API接口没有作对应的安全防御,将可能形成很严重的后果。json
主要防护措施能够概括为两点:跨域
防止重放攻击必需要保证请求仅一次有效
须要经过在请求体中携带当前请求的惟一标识,而且进行签名防止被篡改。
因此防止重放攻击须要创建在防止签名被串改的基础之上。缓存
采用https协议能够将传输的明文进行加密,可是黑客仍然能够截获传输的数据包,进一步伪造请求进行重放攻击。若是黑客使用特殊手段让请求方设备使用了伪造的证书进行通讯,那么https加密的内容也将会被解密。
在API接口中咱们除了使用https协议进行通讯外,还须要有本身的一套加解密机制,对请求参数进行保护,防止被篡改。
过程以下:安全
- 客户端使用约定好的秘钥对传输参数进行加密,获得签名值signature,而且将签名值也放入请求参数中,发送请求给服务端
- 服务端接收客户端的请求,而后使用约定好的秘钥对请求的参数(除了signature之外)再次进行签名,获得签名值autograph。
- 服务端对比signature和autograph的值,若是对比一致,认定为合法请求。若是对比不一致,说明参数被篡改,认定为非法请求。
由于黑客不知道签名的秘钥,因此即便截取到请求数据,对请求参数进行篡改,可是却没法对参数进行签名,没法获得修改后参数的签名值signature。
签名的秘钥咱们可使用不少方案,能够采用对称加密或者非对称加密。服务器
每次HTTP请求,都须要加上timestamp参数,而后把timestamp和其余参数一块儿进行数字签名。由于一次正常的HTTP请求,从发出到达服务器通常都不会超过60s,因此服务器收到HTTP请求以后,首先判断时间戳参数与当前时间相比较,是否超过了60s,若是超过了则认为是非法的请求。xss
通常状况下,黑客从抓包重放请求耗时远远超过了60s,因此此时请求中的timestamp参数已经失效了。
若是黑客修改timestamp参数为当前的时间戳,则signature参数对应的数字签名就会失效,由于黑客不知道签名秘钥,没有办法生成新的数字签名。加密
但这种方式的漏洞也是显而易见的,若是在60s以后进行重放攻击,那就没办法了,因此这种方式不能保证请求仅一次有效。
nonce的意思是仅一次有效的随机字符串,要求每次请求时,该参数要保证不一样,因此该参数通常与时间戳有关,咱们这里为了方便起见,直接使用时间戳的16进制,实际使用时能够加上客户端的ip地址,mac地址等信息作个哈希以后,做为nonce参数。
咱们将每次请求的nonce参数存储到一个“集合”中,能够json格式存储到数据库或缓存中。
每次处理HTTP请求时,首先判断该请求的nonce参数是否在该“集合”中,若是存在则认为是非法请求。
nonce参数在首次请求时,已经被存储到了服务器上的“集合”中,再次发送请求会被识别并拒绝。
nonce参数做为数字签名的一部分,是没法篡改的,由于黑客不清楚token,因此不能生成新的sign。
这种方式也有很大的问题,那就是存储nonce参数的“集合”会愈来愈大,验证nonce是否存在“集合”中的耗时会愈来愈长。咱们不能让nonce“集合”无限大,因此须要按期清理该“集合”,可是一旦该“集合”被清理,咱们就没法验证被清理了的nonce参数了。也就是说,假设该“集合”平均1天清理一次的话,咱们抓取到的该url,虽然当时没法进行重放攻击,可是咱们仍是能够每隔一天进行一次重放攻击的。并且存储24小时内,全部请求的“nonce”参数,也是一笔不小的开销。
nonce的一次性能够解决timestamp参数60s的问题,timestamp能够解决nonce参数“集合”愈来愈大的问题。
防止重放攻击通常和防止请求参数被串改一块儿作,请求的Headers数据以下图所示。
咱们在timestamp方案的基础上,加上nonce参数,由于timstamp参数对于超过60s的请求,都认为非法请求,因此咱们只须要存储60s的nonce参数的“集合”便可。
API接口验证流程:
API重放攻击(Replay Attacks)又称重播攻击、回放攻击。他的原理就是把以前窃听到的数据原封不动的从新发送给接收方。HTTPS并不能防止这种攻击,虽然传输的数据是通过加密的,窃听者没法获得数据的准肯定义,可是能够从请求的接收方地址分析出这些数据的做用。好比用户登陆请求时攻击者虽然没法窃听密码,可是却能够截取加密后的口令而后将其重放,从而利用这种方式进行有效的攻击。
一次HTTP请求,从请求方到接收方中间要通过不少个路由器和交换机,攻击者能够在中途截获请求的数据。假设在一个网上存款系统中,一条消息表示用户支取了一笔存款,攻击者彻底能够屡次发送这条消息而偷窃存款。 重放是二次请求,若是API接口没有作对应的安全防御,将可能形成很严重的后果。 API接口常见的安全防御要作的主要有以下几点:
主要防护措施能够概括为两点:
请求参数防篡改采用https协议能够将传输的明文进行加密,可是黑客仍然能够截获传输的数据包,进一步伪造请求进行重放攻击。若是黑客使用特殊手段让请求方设备使用了伪造的证书进行通讯,那么https加密的内容也将会被解密。
由于黑客不知道签名的秘钥,因此即便截取到请求数据,对请求参数进行篡改,可是却没法对参数进行签名,没法获得修改后参数的签名值signature。 防止重放攻击基于timestamp的方案每次HTTP请求,都须要加上timestamp参数,而后把timestamp和其余参数一块儿进行数字签名。由于一次正常的HTTP请求,从发出到达服务器通常都不会超过60s,因此服务器收到HTTP请求以后,首先判断时间戳参数与当前时间相比较,是否超过了60s,若是超过了则认为是非法的请求。 通常状况下,黑客从抓包重放请求耗时远远超过了60s,因此此时请求中的timestamp参数已经失效了。 但这种方式的漏洞也是显而易见的,若是在60s以后进行重放攻击,那就没办法了,因此这种方式不能保证请求仅一次有效。 基于nonce的方案nonce的意思是仅一次有效的随机字符串,要求每次请求时,该参数要保证不一样,因此该参数通常与时间戳有关,咱们这里为了方便起见,直接使用时间戳的16进制,实际使用时能够加上客户端的ip地址,mac地址等信息作个哈希以后,做为nonce参数。 nonce参数在首次请求时,已经被存储到了服务器上的“集合”中,再次发送请求会被识别并拒绝。 这种方式也有很大的问题,那就是存储nonce参数的“集合”会愈来愈大,验证nonce是否存在“集合”中的耗时会愈来愈长。咱们不能让nonce“集合”无限大,因此须要按期清理该“集合”,可是一旦该“集合”被清理,咱们就没法验证被清理了的nonce参数了。也就是说,假设该“集合”平均1天清理一次的话,咱们抓取到的该url,虽然当时没法进行重放攻击,可是咱们仍是能够每隔一天进行一次重放攻击的。并且存储24小时内,全部请求的“nonce”参数,也是一笔不小的开销。 基于timestamp和nonce的方案nonce的一次性能够解决timestamp参数60s的问题,timestamp能够解决nonce参数“集合”愈来愈大的问题。 咱们在timestamp方案的基础上,加上nonce参数,由于timstamp参数对于超过60s的请求,都认为非法请求,因此咱们只须要存储60s的nonce参数的“集合”便可。
基于以上的方案就能够作到防止API接收的参数被篡改和防止API请求重放攻击。 |
// 获取token String token = request.getHeader("token"); // 获取时间戳 String timestamp = request.getHeader("timestamp"); // 获取随机字符串 String nonceStr = request.getHeader("nonceStr"); // 获取请求地址 String url = request.getHeader("url"); // 获取签名 String signature = request.getHeader("signature"); // 判断参数是否为空 if (StringUtils.isBlank(token) || StringUtils.isBlank(timestamp) || StringUtils.isBlank(nonceStr) || StringUtils.isBlank(url) || StringUtils.isBlank(signature)) { //非法请求 return; } //验证token有效性,获得用户信息 UserTokenInfo userTokenInfo = TokenUtils.getUserTokenInfo(token); if (userTokenInfo == null) { //token认证失败(防止token伪造) return; } // 判断请求的url参数是否正确 if (!request.getRequestURI().equals(url)){ //非法请求 (防止跨域攻击) return; } // 判断时间是否大于60秒 if(DateUtils.getSecond()-DateUtils.toSecond(timestamp)>60){ //请求超时(防止重放攻击) return; } // 判断该用户的nonceStr参数是否已经在redis中 if (RedisUtils.haveNonceStr(userTokenInfo,nonceStr)){ //请求仅一次有效(防止短期内的重放攻击) return; } // 对请求头参数进行签名 String stringB = SignUtil.signature(token, timestamp, nonceStr, url,request); // 若是签名验证不经过 if (!signature.equals(stringB)) { //非法请求(防止请求参数被篡改) return; } // 将本次用户请求的nonceStr参数存到redis中设置60秒后自动删除 RedisUtils.saveNonceStr(userTokenInfo,nonceStr,60); //开始处理合法的请求 |
基于以上的方案就能够作到防止API接收的参数被篡改和防止API请求重放攻击。