TCP数据段格式说明
TCP建立连接和断开连接细节
Https如何保证通信安全
一次Https网络请求通信细节
网络数据包分析工具wireshark的使用
问题:
SYN、ACK、FIN具体含义是什么?
TCP建立连接超时的表现?
为什么需要证书来下发服务端公钥?
客户端是如何验证证书合法性的?
对称秘钥是如何协商出来的?
为什么不直接让客户端自己生成一个秘钥发送给服务端使用?
TLS如何避免重放攻击?
TCP数据包格式说明
TCP数据段分为首部+数据两部分。
首部又分为固定首部和可选项首部。
通过对TCP数据包格式的分析,就是了解TCP协议定义的过程。
来源连接端口(16比特位)-辨识发送连接端口
目的连接端口(16比特位)-辨识接收连接端口
序列号(seq,32比特位)-TCP数据包标识。
无惧传输时的乱序或丢包
建立连接时发送和接收方第一次数据段的seq均为随机生成(TCP序号预测攻击),之后是上次序列号加1
发送数据时seq为上一次发送的数据长度加1,如果数据长度为0则seq不变
确认号(ack,32比特位)-表示接收方期望下次收到的数据包的序列号的值,也是当前收到的数据的字节长度加1
数据偏移(4比特位)-以4字节为单位计算出的数据段开始地址的偏移值,例1111 -> 15 -> 60字节
保留位(3比特位)-需置为0
标志符(9比特位)
NS:Nonce Sum 随机和,
CWR:Congestion Window Reduced 拥塞窗口减少标志被发送主机设置,用来表明它接收到了设置ECE标志的TCP包。拥塞窗口是被TCP维护的一个内部变量,用来管理发送窗口大小
ECE:ECN-Echo(显式拥塞通知回显) ECN响应标志被用来在TCP3次握手时表明一个TCP端是具备ECN功能的,并且表明接收到的TCP包的IP头部的ECN被设置为11 NS/CWR/ECE三个标志组合实现估计网络拥塞情况的功能
URG:为1表示高优先级数据包,紧急指针字段有效
ACK:为1表示确认号字段有效
PSH:为1表示是带有PUSH标志的数据,指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满
RST:为1表示出现严重差错,可能需要重新创建TCP连接,还可以用于拒绝分发的报文段和拒绝连接请求
SYN:为1表示这是连接请求或是连接接受请求,用于创建连接和使顺序号同步
FIN:为1表示发送方没有数据要传输了,要求释放连接
窗口(WIN 16比特位)-表示从确认号开始,本报文的接收方可以接收的字节数,即接收窗口大小,用于流量控制
校验和(Checksum 16比特位) -对整个TCP报文段,包括TCP首部和TCP数据,计算出来的16位值,这是一个强制字段。校验方式为:将TCP报文段的头部和数据部分的和计算出来,再对其求反码
紧急指针(16比特位)-本报文段中的紧急数据的最后一个字节的序号
选项字段 -最多40字节,每个选项的开始是1字节的kind字段,说明选项的类型。下面具体说明支持的选项字段类型
0:(1字节)选项表结束
1:无操作(1字节)用于选项字段之间的字节边界对齐和分割不同的选项数据
2:最大报文长度(4字节,Maximum segment size,MSS) -在握手阶段告知接收方,发送方支持的最大报文数据段的长度,以太网一般为1460。只能出现在同步报文段中,否则将被忽略。通常将MSS设置为MTU-40字节(Maximum Transmission Unit 最大传输单元),这样携带TCP报文段的IP数据报的长度就不会超过MTU,从而避免本机发生IP分片。
3:窗口扩大因子(3字节,wscale)-取值0-14,用来把TCP的窗口的值左移的位数。只能出现在同步报文中,否则将被忽略。这是因为现在的TCP接收数据缓冲区(接收窗口)的长度通常大于65535字节
4:sackOK -发送端支持并同意使用SACK(Selective Acknowledgements,选择确认)选项
5:SACK实际工作的选项,存放丢包的边界信息,最多存放4个包的边界信息。例如123,丢了2,那么SACK存放的就是2号包的开始和结束的字节序列号
8:时间戳(10字节,TCP Timestamps Option,TSopt)
发送端的时间戳(Timestamp Value field, TSval,4字节)
时间戳回显应答(Timestamp Echo Reply field,TSecr,4字节)
时间戳的功能有两个:
用来计算往返时间RTT(Round-Trip Time),发送方在收到确认报文后,可以准确计算出RTT。
防止回绕的序列号,通过时间戳可以判断出相同序列号的数据报,哪个是最近发送的,哪个是以前发送的。
滑动窗口
简单一句话就是接收方可以动态控制发送方下次发送的TCP包数据段的大小。
当接收方处理数据较慢时,就可以通过WIN字段,在ACK包中告知发送方:“以后的数据段少发一些,我处理不过来了。”
在极端情况下,接收方连1字节的数据也不能处理了,那么WIN字段就设置为0,发送方就会停止发送后续的TCP包。
那么,当接收方缓过气来,可以处理更多数据时,发送方是怎么知道的呢?答案是Zero Window Probe(零窗口探针)技术。
发送方会在一定时间间隔内重复发送ZWP包,这时接收方就有机会告知发送方最新的窗口大小。
又有极端情况,接收方一直返回WIN为0,那么发送方在发送一定次数的ZWP后,就会发送RST包来断开连接(不同的系统有不同的实现)。
另外一种极端情况,接收方返回的WIN值特别小,相对于TCP的首部来说,发送较少的数据时一种浪费。这个时候接收方就会使用David D Clark’s 方案。接收方直接返回WIN为0,知道接收方有足够的能力处理新数据时再把WIN打开。
如果是由于发送方发送的数据特别少引起的,那么发送方就会使用Nagle’s algorithm。将多个小的数据包缓存起来,直到满足发送条件。
ECE和CWR
当两个支持ECN的TCP端进行TCP连接时,对于支持ECN的TCP端来说,SYN包的ECE和CWR标志都被设置了。SYN-ACK只设置ECE标志。
一个支持ECN的TCP主机在支持ECN的TCP连接上发送设置了IP头部为10或者01的TCP包。支持ECN的路由器在经历拥塞时设置IP头部的ECN域为11。当一个TCP接收端发送针对收到的一个设置ECN位为11的TCP包的响应时,它设置TCP包头中的ECE,并且在接下来的ACK中也做同样设置。
当发送主机接收到设置了ECE标志的ACK时,它就像感知到包丢失一样,开始减少发送窗口,运行慢启动过程和拥塞避免算法。在下一个数据包中,发送者设置CWR标志。在接收到新的设置CWR标志的包时,接受者停止在接下来的ACK中设置ECE标志。
超时重传
建立连接时:
如果发送方在发送一个SYN包后,在超时时间内没有收到确认包,则发送方会重新发送,称为超时重发。
默认Linux重试次数为5次,重试时间间隔由1s开始每次翻倍,即1s,2s,4s,8s,16s。如果经过1+2+4+8+16+32=63s后,仍没有收到确认包,则发送方认为接收方已掉线,会主动断开当前连接。
数据传输时:
为了网络整体的稳定,需要动态的根据往返时间设置数据包的超时时间。这里就不展开说具体算法过程了。
RTO(Retransmission Timeout)重传超时时间
RTT(Round-Trip Time)往返时间
拥塞控制
慢启动
拥塞避免
快速重传
快速恢复
三次握手 和 四次挥手
三次握手
这是简单的三次握手流程示意图,三次握手意思是需要在发送方和接收方之间传递三个数据包。通过设置不同的标识位,来告知对方当前数据包的意图。
第一次:C发送一个数据包P1给S,并将标识位的SYN置为1,表明“我要和你建立连接”。
第二次:S如果可以接受C的请求,会给C回发一个数据包P2,并将标识位SYN置为1,表明“我同意和你建立连接”。同时将ACK位置为1,表明“确认号ack”字段有效,其值为P1数据包序列号+1。
第三次:C接到P2后,会再次向S发送数据包P3,将ACK为置为1,其值为P2数据包的序列号+1,表明“我知道了你同意了”。
至此,连接就被建立完成了,双方就可以任意发送数据了。
但是,在三次握手过程中,除了要协商连接的建立,还有其他通讯参数的设置。下面以一个真实请求在三次握手过程中发生的数据交换作说明:
第一次:
第二次:
第三次:
四次挥手
这是TCP连接断开时四次挥手的示意图。划重点:
每一侧的连接都单独的被终止。
主动终止连接的一方在接收到ACK后,不能再发送数据,但可以接收数据,也就是半双工状态。
首先终止连接的一方,在给对方响应了ACK后,就会等待2*MSL(Max Segment Lifetime 报文最大生存时间)时间,然后关闭连接。RFC793定义了MSL为2分钟,Linux设置成了30s。
四次挥手也可以通过三次握手实现,即主机A发出FIN,主机B回复FIN&ACK,主机A回复ACK。
下面是三次握手实现的连接断开的报文传输细节:
第一次:
第二次:
第三次:
Https如何保证通信安全
简介
Https 超文本传输安全协议(Hypertext Transfer Protocol Secure),1994年由网景公司提出,Https经由Http进行通信,但利用SSL/TLS来加密数据,即在Http协议与TCP协议之间添加SSL/TLS层。
安全防护:
身份认证,防止中间人攻击
消息加密,防止被窃听
消息校验,防止被篡改
安全前提:
系统或浏览器正确的实现了Https并安装了正确的证书颁发机构
安全通信流程
对称加密
算法公开(AES)
一个秘钥,秘钥不公开
加解密速度快
非对称加密
算法公开(RSA)
两个秘钥,公钥公开,私钥不公开
公钥加密的数据,私钥可以解密。私钥加密的数据,公钥可以解密
加解密速度慢
非对称加密算法除了可以直接将隐私数据加密外,还可以实现对非隐私数据的防篡改校验功能,也就是数字签名。
Hash/散列/摘要算法
以任意长度的数据为输入,输出固定长度的数字“指纹”。
MAC,消息认证码,是带秘钥的Hash算法,即在对数据计算散列值时将秘钥和数据同时作为输入,并采用二次散列迭代的方式。
Alice和Bob
在没有SSL/TLS的世界里,Alice和Bob的通讯是这样的。
Alice(i love you)–明文(i love you)–>Bob(i love you)
一些坏人可以在明文的传输过程中,对数据进行更改。
Alice(i love you)–>坏人(i hate you)–>Bob(i hate you)
Bob(i still love you )–>坏人(i hate you too)–>Alice(i hate you too)
当两人见面后,发现对方误会了自己,就想到这个世界还是有坏人,然后双方约定将通信内容加密后再发送给对方。约定的加密算法为AES,秘钥为“Alice/Bob”。
加密后就会出现两种情况:
Alice(i love you)–加密(123abc)–>坏人(123abc)–>Bob(123abc)–>解密(i love you)
Alice(i love you)–加密(123abc)–>坏人(!!!)–>Bob(!!!)–>解密(???)
单纯使用对称加密会有两个问题:
坏人虽然不能窃听内容,但是仍能篡改。
对称加密的秘钥只能Alice和Bob两个人知道,如果想再和更多的人加密通信的话,就无能为力了。
Alice和Bob必须在通信之前就约定好秘钥(在互联网世界中办不到),中途无法更新。
所以,我们要解决这些问题,需要做到:
为了过滤掉被篡改的数据,通信过程需要有内容校验机制
为了可以和无限多人通信,需要能动态生成和更新秘钥
Alice和Bob非常聪明,他们想到了非对称加密算法RSA,Bob拿着私钥,然后把公钥给Alice,当Alice想要和Bob通信时,利用手中的公钥将对称加密算法的秘钥加密后发送给Bob,Bob拿着自己的私钥将对称加密的秘钥解密后,双方就可以继续用对称加密算法将数据加密后通信了。当然,其他人也可以拿到Bob的公钥,与Bob通信。
Alice(我想和你说话,给我公钥)------->Bob(我想和你说话,给我公钥)
Bob(给你公钥(Pubkey)) --------->Alice(拿到公钥PubKey)
Alice(秘钥:123)–>公钥PubKey加密(321)–>Bob(321)–>私钥解密(123)
Alice(i love you)–>秘钥123加密(123abc+摘要签名)---->Bob(123abc+摘要签名)–>正确性验证–>秘钥123解密(i love you)
上述第二个过程是无懈可击的,但是第一个过程中,如果存在坏人的话,就变成下面的情形
Bob(给你公钥(PubKey))---->坏人(PubKey换成自己的PubKey1)--------->Alice(拿到公钥PubKey1)
Alice(秘钥:123)–>公钥PubKey1加密(abc)–>坏人(abc),自己的私钥解密123,再用PubKey加密321–>Bob(321)–>私钥解密(123)
完犊子了,对称加密的秘钥被坏人窃取了,通信数据又相当于裸奔了。其中核心问题就是:
接收方无法确定公钥的合法性
那如果把公钥事先告知Alice可以么?也就是内置在系统中。理论上是没问题的,但当Alice需要和更多的人通信时,她需要记住很多很多的公钥,这是不可行的。
所以Alice和Bob需要商量出一套方案,能保证公钥在网络上安全的传输,如果受到篡改,接收方能感知到。这时,他们想到数字签名的方式。
Bob(给你公钥+签名(摘要的私钥加密))------>Alice(公钥+签名,对签名解密,并再次计算摘要,然后比对)
上述方式可以做到防篡改么?可以,但是做不到防替换。中间人可以把签名连同公钥全部换成自己的。
接下来就到了数字签名证书出场的时刻了。
Bob找到了一个非常权威的机构,“人民*”。
Bob向“人民*”证明“我是真Bob”,并提供自己的公钥。
“人民*”根据Bob的信息和公钥颁发给Bob一个证书文件.cer,里面写了颁发机构的信息、Bob的信息和公钥、摘要算法以及最重要的颁发机构的签名。
公钥下发过程变成了数字证书下发过程。
同时,Alice也是非常相信“人民*”的,只要是“人民*”签名的证书,Alice就认为证书上面的公钥就是Bob的。
但是,Alice并不能无脑的相信,她需要判断了两点:
接收到的证书是否是”人民*“签发的。
证书上面的信息是否被篡改。
那么具体的判断流程是怎样的呢?
由于Alice信任”人民*“这个机构,所以Alice可以内置一份”人民*“自签名的证书,上面有”人民*“生成的公私钥中的公钥,私钥由”人民*“自己保管。
在下发证书过程中,Alice拿到Bob发过来的由”人民*“私钥签名过得证书cert0。
Alice首先根据cert0上的证书颁发机构信息判断自己是否信任这个机构颁发的证书,cert0是由”人民*“签发的,而Alice是信任”人民*“的,所以Alice信任cert0。
Alice通过查找内置的”人民*“的自签名证书拿到”人民*“的公钥
用此公钥验证cert0上的信息是否被篡改
问题:Bob的公钥不是由”人民*“签发的,而是由其下属的”地方*“签发的。而Alice只有”人民*“的自签名*,如何判断证书合法性呢?
答:在证书下发过程中,实际是下发的一个证书链,类似于”Bob的证书“–>“地方*的证书”–>”人民*的证书“,Alice可以逐级查找,直到根证书。
证书内容
Https证书格式遵循的是X.509标准。X.509是ITU-T标准化部门基于他们之前的ASN.1定义的一套证书标准。
在浏览器中随便下载一个证书,通过以下命令获取到其文本格式。
openssl x509 -in *.xxx.com.cer -inform der -text -noout >> cer.txt
Certificate: Data: Version: 3 (0x2) Serial Number: 09:3e:8a:aa:5a:f8:14:de:9d:d9:4d:28:2e:97:a8:16 Signature Algorithm: sha256WithRSAEncryption Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=GeoTrust RSA CA 2018 //颁发者信息 Validity Not Before: Aug 22 00:00:00 2018 GMT Not After : Nov 12 12:00:00 2020 GMT Subject: C=CN, ST=\xE5\x8C\x97\xE4\xBA\xAC, L=\xE5\x8C\x97\xE4\xBA\xAC, O=Beijing Qfpay Technology Co., ltd., CN=*.qfpay.com //公钥主题信息,公钥所有者的信息 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:e6:c0:8b:f0:12:1e:f0:92:9c:17:7d:7e:d5:69: 47:fc:dd:0b:51:5a:0e:79:df:b1:0e:b4:d6:7d:d0: 5a:bc:f9:93:f6:3c:e3:40:a8:66:9f:d0:ae:3c:e1: f8:9a:55:a2:84:0e:9c:1d:65:f9:d2:63:51:48:b2: 88:a5:09:a6:be:92:80:f8:3b:eb:b8:78:1b:35:58: 47:ac:eb:47:cd:3d:7f:36:74:30:7a:01:86:48:96: b3:7b:14:82:b6:63:0b:b6:43:20:98:3f:07:9d:1a: 56:76:25:cf:cd:d5:49:fd:6e:dc:86:f0:7f:15:f3: 7d:58:98:75:a5:7f:f9:ab:b2:c4:ec:fc:30:bd:75: 27:b3:0e:72:3d:44:d1:04:42:52:65:9b:3e:53:9b: a5:c2:eb:ac:c5:01:b6:1d:0d:2f:75:79:7d:98:d4: 2b:b6:c0:28:ea:c7:dc:14:04:b6:4d:a3:dc:01:2c: f0:14:13:b9:d2:29:31:00:37:af:17:d6:82:a6:f9: 57:9e:4c:2f:27:27:08:50:16:e3:ca:fa:58:32:c7: f5:04:43:b4:5d:0e:97:81:e9:c3:01:36:f9:b7:c8: 14:ec:98:27:e9:31:86:ab:f5:c4:ff:50:aa:c4:df: cc:6e:7d:1c:5a:fa:b8:47:c9:fa:78:b4:de:6d:15: 8d:27 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: //授权秘钥标识符 keyid:90:58:FF:B0:9C:75:A8:51:54:77:B1:ED:F2:A3:43:16:38:9E:6C:C5 X509v3 Subject Key Identifier: //主题秘钥标识符 EC:99:74:D5:FF:C6:1B:4F:FB:39:88:8C:E2:C1:7B:8D:90:59:AB:1F X509v3 Subject Alternative Name: DNS:*.xxx.com, DNS:xxx.com X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication X509v3 CRL Distribution Points: Full Name: URI:http://cdp1.digicert.com/GeoTrustRSACA2018.crl X509v3 Certificate Policies: Policy: 2.16.840.1.114412.1.1 CPS: https://www.digicert.com/CPS Policy: 2.23.140.1.2.2 Authority Information Access: OCSP - URI:http://ocsp1.digicert.com CA Issuers - URI:http://cacerts.geotrust.com/GeoTrustRSACA2018.crt Signature Algorithm: sha256WithRSAEncryption 07:8d:b9:34:62:e8:4b:83:00:af:ab:38:d4:b1:24:12:a4:37: 5e:8f:e7:ff:d9:96:48:ae:72:6f:d2:0b:41:6a:55:92:2a:06: 39:86:a9:78:18:cd:0d:5f:33:fa:22:81:50:b7:67:2f:dc:a1: b4:ee:0f:6c:f8:73:87:0d:65:e7:19:9a:55:07:49:a4:2d:09: 11:8b:5f:1c:c1:46:ce:94:22:fa:b0:1b:88:f0:f0:6f:63:11: e4:56:f4:51:3c:12:90:db:44:63:8b:fd:17:d2:e5:7a:66:5e: f5:d8:90:70:5c:d6:c2:74:d9:74:b3:75:ce:83:e3:db:57:bb: b6:3a:81:e7:ca:7a:48:82:6c:0b:01:a8:ed:a2:8e:d0:b0:ed: 25:15:a2:2a:7f:6f:a5:6d:da:5a:ac:91:f4:dc:23:d8:9f:9d: d3:0a:f4:c7:8f:b0:c2:18:54:97:f5:00:30:36:65:e1:aa:25: 9c:f1:b8:77:d6:7d:33:79:39:0e:41:86:1d:79:47:0e:34:cc: fd:e8:63:83:9d:f5:86:d6:e2:0c:fa:58:d5:d2:81:c1:92:da: e7:41:45:bc:a0:91:d5:40:6e:c8:22:76:69:e4:67:9a:d4:03: ca:8d:28:d5:ca:98:09:e0:d6:dd:ae:c2:6f:08:82:1b:89:79: 14:d6:ca:b7
SSL/TLS握手细节
SSL/TLS报文格式
SSL协议属于分层协议,一个SSL报文可以包含多个记录层,每个记录层分为两部分:头部 + 协议数据。
Content Type:协议类型
Version:TLS版本
Length:报文长度
协议数据,不同的协议包含数据字段不同
报文支持的协议类型
Handshake Protocol
握手协议是最主要的协议,负责协商会话的安全属性。按照不同的功能区分为不同握手类型。
1 enum { 2 hello_request(0), client_hello(1), server_hello(2), 3 certificate(11), server_key_exchange (12), 4 certificate_request(13), server_hello_done(14), 5 certificate_verify(15), client_key_exchange(16), 6 finished(20), (255) 7 } HandshakeType; 8 9 struct { 10 HandshakeType msg_type; /* handshake type */ 11 uint24 length; /* bytes in message */ 12 select (HandshakeType) { 13 case hello_request: HelloRequest; 14 case client_hello: ClientHello; 15 case server_hello: ServerHello; 16 case certificate: Certificate; 17 case server_key_exchange: ServerKeyExchange; 18 case certificate_request: CertificateRequest; 19 case server_hello_done: ServerHelloDone; 20 case certificate_verify: CertificateVerify; 21 case client_key_exchange: ClientKeyExchange; 22 case finished: Finished; 23 } body; 24 } Handshake;
每种握手协议类型,也都包含一些通用字段,如
HandshakeType
Version
Length
下面具体说明每种握手类型的作用:
Hello Request
”你好,请求“,该类型的握手协议作为一个简单的通知,由服务端发送,告知客户端重新开始协商过程。作为响应,客户端应该在方便时发送ClientHello消息。
如果客户端不希望重新协商会话,可以选择忽略此消息,或者返回一个no_renegotitation消息。
如果服务端发送HelloRequest后没有收到ClientHello作为响应,它可能会通过致命警报关闭连接。
Client Hello
”你好,我是客户端“,该类型的消息用于首次连接服务器时,或响应服务端发送的HelloRequest,或主动发送,以便重新协商连接中的安全参数。