API接口防止参数篡改和重放攻击

API重放攻击(Replay Attacks)又称重播攻击、回放攻击。他的原理就是把以前窃听到的数据原封不动的从新发送给接收方。HTTPS并不能防止这种攻击,虽然传输的数据是通过加密的,窃听者没法获得数据的准肯定义,可是能够从请求的接收方地址分析出这些数据的做用。好比用户登陆请求时攻击者虽然没法窃听密码,可是却能够截取加密后的口令而后将其重放,从而利用这种方式进行有效的攻击。redis

所谓重放攻击就是攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程,重放攻击是计算机世界黑客经常使用的攻击方式之一。sql

一次HTTP请求,从请求方到接收方中间要通过不少个路由器和交换机,攻击者能够在中途截获请求的数据。假设在一个网上存款系统中,一条消息表示用户支取了一笔存款,攻击者彻底能够屡次发送这条消息而偷窃存款。数据库

重放是二次请求,若是API接口没有作对应的安全防御,将可能形成很严重的后果。json

API接口常见的安全防御要作的主要有以下几点:

  • 防止sql注入
  • 防止xss攻击
  • 防止请求参数被串改
  • 防止重放攻击

主要防护措施能够概括为两点:跨域

  • 对请求的合法性进行校验
  • 对请求的数据进行校验

防止重放攻击必需要保证请求仅一次有效
须要经过在请求体中携带当前请求的惟一标识,而且进行签名防止被篡改。
因此防止重放攻击须要创建在防止签名被串改的基础之上。缓存

请求参数防篡改

采用https协议能够将传输的明文进行加密,可是黑客仍然能够截获传输的数据包,进一步伪造请求进行重放攻击。若是黑客使用特殊手段让请求方设备使用了伪造的证书进行通讯,那么https加密的内容也将会被解密。
在API接口中咱们除了使用https协议进行通讯外,还须要有本身的一套加解密机制,对请求参数进行保护,防止被篡改。
过程以下:安全

  1. 客户端使用约定好的秘钥对传输参数进行加密,获得签名值signature,而且将签名值也放入请求参数中,发送请求给服务端
  2. 服务端接收客户端的请求,而后使用约定好的秘钥对请求的参数(除了signature之外)再次进行签名,获得签名值autograph。
  3. 服务端对比signature和autograph的值,若是对比一致,认定为合法请求。若是对比不一致,说明参数被篡改,认定为非法请求。

由于黑客不知道签名的秘钥,因此即便截取到请求数据,对请求参数进行篡改,可是却没法对参数进行签名,没法获得修改后参数的签名值signature。
签名的秘钥咱们可使用不少方案,能够采用对称加密或者非对称加密。服务器

防止重放攻击

基于timestamp的方案

每次HTTP请求,都须要加上timestamp参数,而后把timestamp和其余参数一块儿进行数字签名。由于一次正常的HTTP请求,从发出到达服务器通常都不会超过60s,因此服务器收到HTTP请求以后,首先判断时间戳参数与当前时间相比较,是否超过了60s,若是超过了则认为是非法的请求。xss

通常状况下,黑客从抓包重放请求耗时远远超过了60s,因此此时请求中的timestamp参数已经失效了。
若是黑客修改timestamp参数为当前的时间戳,则signature参数对应的数字签名就会失效,由于黑客不知道签名秘钥,没有办法生成新的数字签名。加密

但这种方式的漏洞也是显而易见的,若是在60s以后进行重放攻击,那就没办法了,因此这种方式不能保证请求仅一次有效。

基于nonce的方案

nonce的意思是仅一次有效的随机字符串,要求每次请求时,该参数要保证不一样,因此该参数通常与时间戳有关,咱们这里为了方便起见,直接使用时间戳的16进制,实际使用时能够加上客户端的ip地址,mac地址等信息作个哈希以后,做为nonce参数。
咱们将每次请求的nonce参数存储到一个“集合”中,能够json格式存储到数据库或缓存中。
每次处理HTTP请求时,首先判断该请求的nonce参数是否在该“集合”中,若是存在则认为是非法请求。

nonce参数在首次请求时,已经被存储到了服务器上的“集合”中,再次发送请求会被识别并拒绝。
nonce参数做为数字签名的一部分,是没法篡改的,由于黑客不清楚token,因此不能生成新的sign。

这种方式也有很大的问题,那就是存储nonce参数的“集合”会愈来愈大,验证nonce是否存在“集合”中的耗时会愈来愈长。咱们不能让nonce“集合”无限大,因此须要按期清理该“集合”,可是一旦该“集合”被清理,咱们就没法验证被清理了的nonce参数了。也就是说,假设该“集合”平均1天清理一次的话,咱们抓取到的该url,虽然当时没法进行重放攻击,可是咱们仍是能够每隔一天进行一次重放攻击的。并且存储24小时内,全部请求的“nonce”参数,也是一笔不小的开销。

基于timestamp和nonce的方案

nonce的一次性能够解决timestamp参数60s的问题,timestamp能够解决nonce参数“集合”愈来愈大的问题。
防止重放攻击通常和防止请求参数被串改一块儿作,请求的Headers数据以下图所示。

咱们在timestamp方案的基础上,加上nonce参数,由于timstamp参数对于超过60s的请求,都认为非法请求,因此咱们只须要存储60s的nonce参数的“集合”便可。
API接口验证流程:

API重放攻击(Replay Attacks)又称重播攻击、回放攻击。他的原理就是把以前窃听到的数据原封不动的从新发送给接收方。HTTPS并不能防止这种攻击,虽然传输的数据是通过加密的,窃听者没法获得数据的准肯定义,可是能够从请求的接收方地址分析出这些数据的做用。好比用户登陆请求时攻击者虽然没法窃听密码,可是却能够截取加密后的口令而后将其重放,从而利用这种方式进行有效的攻击。

所谓重放攻击就是攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程,重放攻击是计算机世界黑客经常使用的攻击方式之一。

一次HTTP请求,从请求方到接收方中间要通过不少个路由器和交换机,攻击者能够在中途截获请求的数据。假设在一个网上存款系统中,一条消息表示用户支取了一笔存款,攻击者彻底能够屡次发送这条消息而偷窃存款。

重放是二次请求,若是API接口没有作对应的安全防御,将可能形成很严重的后果。

API接口常见的安全防御要作的主要有以下几点:

  • 防止sql注入
  • 防止xss攻击
  • 防止请求参数被串改
  • 防止重放攻击

主要防护措施能够概括为两点:

  • 对请求的合法性进行校验
  • 对请求的数据进行校验

防止重放攻击必需要保证请求仅一次有效
须要经过在请求体中携带当前请求的惟一标识,而且进行签名防止被篡改。
因此防止重放攻击须要创建在防止签名被串改的基础之上。

请求参数防篡改

采用https协议能够将传输的明文进行加密,可是黑客仍然能够截获传输的数据包,进一步伪造请求进行重放攻击。若是黑客使用特殊手段让请求方设备使用了伪造的证书进行通讯,那么https加密的内容也将会被解密。
在API接口中咱们除了使用https协议进行通讯外,还须要有本身的一套加解密机制,对请求参数进行保护,防止被篡改。
过程以下:

  1. 客户端使用约定好的秘钥对传输参数进行加密,获得签名值signature,而且将签名值也放入请求参数中,发送请求给服务端
  2. 服务端接收客户端的请求,而后使用约定好的秘钥对请求的参数(除了signature之外)再次进行签名,获得签名值autograph。
  3. 服务端对比signature和autograph的值,若是对比一致,认定为合法请求。若是对比不一致,说明参数被篡改,认定为非法请求。

由于黑客不知道签名的秘钥,因此即便截取到请求数据,对请求参数进行篡改,可是却没法对参数进行签名,没法获得修改后参数的签名值signature。
签名的秘钥咱们可使用不少方案,能够采用对称加密或者非对称加密。

防止重放攻击

基于timestamp的方案

每次HTTP请求,都须要加上timestamp参数,而后把timestamp和其余参数一块儿进行数字签名。由于一次正常的HTTP请求,从发出到达服务器通常都不会超过60s,因此服务器收到HTTP请求以后,首先判断时间戳参数与当前时间相比较,是否超过了60s,若是超过了则认为是非法的请求。

通常状况下,黑客从抓包重放请求耗时远远超过了60s,因此此时请求中的timestamp参数已经失效了。
若是黑客修改timestamp参数为当前的时间戳,则signature参数对应的数字签名就会失效,由于黑客不知道签名秘钥,没有办法生成新的数字签名。

但这种方式的漏洞也是显而易见的,若是在60s以后进行重放攻击,那就没办法了,因此这种方式不能保证请求仅一次有效。

基于nonce的方案

nonce的意思是仅一次有效的随机字符串,要求每次请求时,该参数要保证不一样,因此该参数通常与时间戳有关,咱们这里为了方便起见,直接使用时间戳的16进制,实际使用时能够加上客户端的ip地址,mac地址等信息作个哈希以后,做为nonce参数。
咱们将每次请求的nonce参数存储到一个“集合”中,能够json格式存储到数据库或缓存中。
每次处理HTTP请求时,首先判断该请求的nonce参数是否在该“集合”中,若是存在则认为是非法请求。

nonce参数在首次请求时,已经被存储到了服务器上的“集合”中,再次发送请求会被识别并拒绝。
nonce参数做为数字签名的一部分,是没法篡改的,由于黑客不清楚token,因此不能生成新的sign。

这种方式也有很大的问题,那就是存储nonce参数的“集合”会愈来愈大,验证nonce是否存在“集合”中的耗时会愈来愈长。咱们不能让nonce“集合”无限大,因此须要按期清理该“集合”,可是一旦该“集合”被清理,咱们就没法验证被清理了的nonce参数了。也就是说,假设该“集合”平均1天清理一次的话,咱们抓取到的该url,虽然当时没法进行重放攻击,可是咱们仍是能够每隔一天进行一次重放攻击的。并且存储24小时内,全部请求的“nonce”参数,也是一笔不小的开销。

基于timestamp和nonce的方案

nonce的一次性能够解决timestamp参数60s的问题,timestamp能够解决nonce参数“集合”愈来愈大的问题。
防止重放攻击通常和防止请求参数被串改一块儿作,请求的Headers数据以下图所示。

咱们在timestamp方案的基础上,加上nonce参数,由于timstamp参数对于超过60s的请求,都认为非法请求,因此咱们只须要存储60s的nonce参数的“集合”便可。
API接口验证流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 获取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请求重放攻击。

// 获取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请求重放攻击。

相关文章
相关标签/搜索