1.为什么要有网络安全协议?
随着网络安全问题日益严重,原始的用于实现终端之间数据传输过程的协议,暴露出以下问题。为了弥补网络协议安全性方面的先天性不足,进一步加剧了网络安全问题,因此需要在原有网络协议的基础上,增加一套用于实现双向鉴别,数据加密传输和数据传输完整性的协议这些就是网络安全协议。
1.1存在的问题
1.1.1源端鉴别问题
在互联网中IP地址为终端的唯一标识,但是IP分组首部中的源IP地址是可以伪造的,大量源IP地址欺骗攻击都是通过伪造源IP地址实施的,所以不可以仅仅通过IP分组首部中的源IP地址来确认该IP分株的源终端,需要更加安全有效的源鉴别机制。
1.1.2数据传输的保密性问题
网络传输中的IP分组没有进行加密,且网络中存在各种用于嗅探,截获IP分组的攻击手段。如果IP分组被截获就会导致信息泄露。
1.1.3数据传输的完整性问题
由于存在各种截获IP分组的攻击手段,攻击者截获IP分组后,可以篡改IP分组中的信息,然后继续传输篡改后的IP分组,因此需要保障IP分组完整性的机制。
1.1.4身份鉴别问题
网络中存在大量的伪造网站,因此当终端访问某个网站的时候必须确定网站是不是伪造的,这就需要对网站的身份进行鉴别。
1.2解决方案
1.2.1双向身份鉴别
身份鉴别就是一方向另一方证明自己身份的过程,目前主要有以下两种身份鉴别方法,基于共享密钥的身份鉴别和基于证书的身份鉴别。
1.2.1.1基于共享密钥
一方于另一方共享某个密钥k,该密钥k只有双发知道,因此共享密钥k成为双方的身份标识信息,一方只要向另一方发送加密信息,另一方可以解密,就可以证明自己的用于密钥k,并因此向另一方证明自己的身份
1.2.1.2基于证书+私钥
RSA公开密钥加密算法中公钥PK与私钥SK一一对应,因此只要能通过权威机构颁发的证书证明公钥PK与X之间的关联,标识为x的一方通过向另一方发送P||Dsk(MD§)就可以向另一方证明拥有公钥PK对应的私钥SK,并向另一方证明我是x
1.2.2数据加密
为了实现数据加密,需要双方事先约定加密算法,加密密钥等,因此,安全协议需要实现密钥分发,加密算法协商等功能,数据加密用于保证数据的保密性
1.2.3数据完整性检测
为了实现数据完整性检测,双发需要事先约定报文摘要算法,加密算法,加密密钥等等,因此安全协议需要实现密钥分发,报文摘要算法,加密算法协商等功能。数据完整性用于检测保证数据的完整性,同时实现源端鉴别。
1.2.4防止重放攻击
首先要明确一个事情,重放攻击是二次请求,黑客通过抓包获取到了请求的HTTP报文,然后黑客自己编写了一个类似的HTTP请求,发送给服务器。也就是说服务器处理了两个请求,先处理了正常的HTTP请求,然后又处理了黑客发送的篡改过的HTTP请求。
防止重放攻击的方式
1. 加随机数
该方法优点是认证双方不需要时间同步,双方记住使用过的随机数,如发现报文中有以前使用过的随机数,就认为是重放攻击。缺点是需要额外保存使用过的随机数,若记录的时间段较长,则保存和查询的开销较大。
2. 加时间戳
**
该方法优点是不用额外保存其他信息。缺点是认证双方需要准确的时间同步,同步越好,受攻击的可能性就越小。但当系统很庞大,跨越的区域较广时,要做到精确的时间同步并不是很容易。
3. 加流水号
**
就是双方在报文中添加一个逐步递增的整数,只要接收到一个不连续的流水号报文(太大或太小),就认定有重放威胁。该方法优点是不需要时间同步,保存的信息量比随机数方式小。缺点是一旦攻击者对报文解密成功,就可以获得流水号,从而每次将流水号递增欺骗认证端。
在实际中,常将方法(1)和方法(2)组合使用,这样就只需保存某个很短时间段内的所有随机数,而且时间戳的同步也不需要太精确。
对付重放攻击除了使用本以上方法外,还可以使用挑战一应答机制和一次性口令机制,而且似乎后面两种方法在实际中使用得更广泛。
作者:进击的前端_风笑影
链接:https://www.jianshu.com/p/7887f16581c0
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
加时间戳方式详解
每次HTTP请求,都需要加上timestamp参数,然后把timestamp和其他参数一起进行数字签名。因为一次正常的HTTP请求,从发出到达服务器一般都不会超过60s,所以服务器收到HTTP请求之后,首先判断时间戳参数与当前时间相比较,是否超过了60s,如果超过了则认为是非法的请求。
假如黑客通过抓包得到了我们的请求url:
http://koastal.site/index/Info?uid=ZX07&stime=1480862753&sign=80b886d71449cb33355d017893720666
其中
s i g n = m d 5 ( sign=md5( sign=md5(uid. t o k e n . token. token.stime);
long uid = 3535353535353535L;
String token = "fewgjiwghwoi3ji4oiwjo34ir4erojwk";
long time = new Date().getTime();//1543991604448
String sign = MD5Utils.MD5Encode("uid=" + uid + "&time=" + time + token,"utf8");
public class MD5Utils {
private static final String hexDigIts[] = {"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"};
/**
* MD5加密
* @param origin 字符
* @param charsetname 编码
* @return
*/
public static String MD5Encode(String origin, String charsetname){
String resultString = null;
try{
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if(null == charsetname || "".equals(charsetname)){
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
}else{
resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
}
}catch (Exception e){
}
return resultString;
}
public static String byteArrayToHexString(byte b[]){
StringBuffer resultSb = new StringBuffer();
for(int i = 0; i < b.length; i++){
resultSb.append(byteToHexString(b[i]));
}
return resultSb.toString();
}
public static String byteToHexString(byte b){
int n = b;
if(n < 0){
n += 256;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigIts[d1] + hexDigIts[d2];
}
}
// 服务器通过uid从数据库中可读出token
然后取出token,通过再次进行md5加密判断二者是否相等,如果相等则说明正确,不是重放攻击
一般情况下,黑客从抓包重放请求耗时远远超过了60s,所以此时请求中的stime参数已经失效了。
如果黑客修改stime参数为当前的时间戳,则sign参数对应的数字签名就会失效,因为黑客不知道token值,没有办法生成新的数字签名。
但这种方式的漏洞也是显而易见的,如果在60s之内进行重放攻击,那就没办法了,所以这种方式不能保证请求仅一次有效。
基于nonce的方案
**
nonce的意思是仅一次有效的随机字符串,要求每次请求时,该参数要保证不同,所以该参数一般与时间戳有关,我们这里为了方便起见,直接使用时间戳的16进制,实际使用时可以加上客户端的ip地址,mac地址等信息做个哈希之后,作为nonce参数。
我们将每次请求的nonce参数存储到一个“集合”中,可以json格式存储到数据库或缓存中。
每次处理HTTP请求时,首先判断该请求的nonce参数是否在该“集合”中,如果存在则认为是非法请求。
假如黑客通过抓包得到了我们的请求url:
http://koastal.site/index/Info?uid=ZX07&nonce=58442c21&sign=80b886d71449cb33355d017893720666
其中
s i g n = m d 5 ( sign=md5( sign=md5(uid. t o k e n . token. token.nonce);
// 服务器通过uid从数据库中可读出token
nonce参数在首次请求时,已经被存储到了服务器上的“集合”中,再次发送请求会被识别并拒绝。
nonce参数作为数字签名的一部分,是无法篡改的,因为黑客不清楚token,所以不能生成新的sign。
这种方式也有很大的问题,那就是存储nonce参数的“集合”会越来越大,验证nonce是否存在“集合”中的耗时会越来越长。我们不能让nonce“集合”无限大,所以需要定期清理该“集合”,但是一旦该“集合”被清理,我们就无法验证被清理了的nonce参数了。也就是说,假设该“集合”平均1天清理一次的话,我们抓取到的该url,虽然当时无法进行重放攻击,但是我们还是可以每隔一天进行一次重放攻击的。而且存储24小时内,所有请求的“nonce”参数,也是一笔不小的开销。
时间戳与nonce结合
那我们如果同时使用timestamp和nonce参数呢?
nonce的一次性可以解决timestamp参数60s的问题,timestamp可以解决nonce参数“集合”越来越大的问题。
我们在timestamp方案的基础上,加上nonce参数,因为timstamp参数对于超过60s的请求,都认为非法请求,所以我们只需要存储60s的nonce参数的“集合”即可。
假如黑客通过抓包得到了我们的请求url:
其中
s i g n = m d 5 ( sign=md5( sign=md5(uid. t o k e n . token. token.stime.$nonce);
// 服务器通过uid从数据库中可读出token
如果在60s内,重放该HTTP请求,因为nonce参数已经在首次请求的时候被记录在服务器的nonce参数“集合”中,所以会被判断为非法请求。超过60s之后,stime参数就会失效,此时因为黑客不清楚token的值,所以无法重新生成签名。
综上,我们认为一次正常的HTTP请求发送不会超过60s,在60s之内的重放攻击可以由nonce参数保证,超过60s的重放攻击可以由stime参数保证。
因为nonce参数只会在60s之内起作用,所以只需要保存60s之内的nonce参数即可。
我们并不一定要每个60s去清理该nonce参数的集合,只需要在新的nonce到来时,判断nonce集合最后一次修改时间,超过60s的话,就清空该集合,存放新的nonce参数集合。其实nonce参数集合可以存放的时间更久一些,但是最少是60s。
随机数集合可以根据业务场景采用定期清理或根据大小自动清理的方案,例如该接口每秒的请求数最高为1000,则60s内的请求数量最多为1500*60=90000,则我们在每次请求后检查集合大小是否超过90000,若超高该数量则清空。
原文链接:https://blog.csdn.net/weixin_39986856/article/details/82657808