TCP/IP 和HTTPS

TCP/IP

网络协议

计算机网络需要解决的一个重要问题就是如何无障碍地发送和接收数据,而发送和接收数据的过程需要相应的协议来支撑,按互相可以理解的方式进行数据的打包与解包,使不同厂商的设备在不同类型的操作系统上实现顺畅的网络通信。

TCP/IP 是传输控制协议/因特网互联协议,这个协议族里还包括 HTTP、HTTPS、FTP、SMTP、UDP、ARP、PPP、IEEE 802.x 等。TCP/IP 是当前流行的网络传输协议框架,从严格意义来说是一个协议族,因为 TCP 和 IP 是其中最为核心的协议,所以命名为 TCP/IP。另一种知名的 OSI 七层网络协议已经被淘汰。

TCP/IP 协议分层框架主要分为五层:物理层、链路层、网络层、传输层、应用层。

  • 链路层: 单个 0 和 1 是没有意义的,链路层以字节为单位把 0 和 1 进行分组,定义数据帧,写入源和目的主机的物理地址、数据、校验位来传输数据。链路层的报文结构包括链路层报头和帧上数据,报头包括目标 MAC 地址、源 MAC 地址和网络层协议。MAC 地址长 6 个字节共 48 位,通常使用 16 进制表示。使用 ifconfig -a 即可看到 MAC 地址,每个地址是全球唯一的。

  • 网络层: 根据 IP 定义网络地址,区分网段。子网内根据地址解析协议 ARP 进行 MAC 寻址,子网外进行路由转发 IP 数据包。

  • 传输层: 数据包通过网络层发送到目标计算机后,应用程序在传输层定义逻辑端口,确认身份后把数据包交给应用程序,实现端口到端口间的通信。最典型的传输层协议是 TCP 和 UDP。UDP 只是在 IP 数据包上增加端口等部分信息,是面向无连接的,属于不可靠传输,多用于视频通信、电话会议等。与 UDP 相反,TCP 是面向连接的,所谓面向连接就是一种端到端之间通过失败重传机制建立的可靠数据传输方式,给人感觉是有一条固定的通路承载着可靠的数据传输。

  • 应用层: 传输层的数据到达应用程序时,以某种统一规定的协议格式解读数据。比如 Email 在各个公司的程序界面、操作、管理方式都不同,但是都能够读取邮件内容,因为 SMTP 协议就像传统的书信格式一样,按规定填写邮编及收信人信息。

程序在发送消息时,应用层按照既定的协议打包数据,随后由传输层加上双方的端口号,由网络层加上双方的 IP 地址,由链路层加上双方的 MAC 地址,并将数据拆分成数据帧,经过多个路由器和网关后到达目标机器。简而言之就是按照"端口->IP地址->MAC 地址"这样的路径进行数据的封装和发送,解包的时候反过来操作即可。


IP 协议

IP 是面向无连接、无状态的,没有额外机制保证发送的包是否有序到达。IP 首先规定出 IP 地址格式,该地址相当于在逻辑意义上进行了网段的划分,给每台计算机设置了一个唯一的详细地址。链路层可以通过 MAC 地址找到机器,但仍然需要一个唯一的 IP 地址来标识机器,这是因为在世界范围内不可能通过广播的方式来找到目标 MAC 地址的计算机而不超时。在数据投递时就需要对地址进行分层管理,例如从美国寄快递给中国陕西省西安市的我,快递公司需要先确定中国的转运公司,再从转运公司逐级配送到各个下级转运点。

IP 地址属于网络层,主要功能在 WLAN 内进行路由寻址、选择最佳路由。IP 报文结构也是由 IP 报头和 IP 数据组成。

生存时间 TTL 表示 IP 报文被路由器丢弃之前可经过的最多路由总数,TTL 初始值由源主机设置后,数据包在传输过程中每经过一个路由器 TTL 值则减 1,当该字段为 0 时,数据包被丢弃,并发送 ICMP 报文通知源主机,以防止源主机无休止地发送报文。

扫描二维码关注公众号,回复: 11354186 查看本文章

ICMP 是检测传输网络是否通畅、主机是否可达、路由是否可达等网络运行状态的协议,ICMP 虽然并不传输用户数据,但是对评估网络健康状态非常重要,经常使用的 ping、tracert 命令就是基于 ICMP 检测网络状态的工具。挂载协议标识表示 IP 数据包里放置的子数据包协议类型,如 6 代表 TCP,17 代表 UDP。

IP 报文在互联网上传输时,可能要经历多个网络,才能从源主机到达目标主机。比如在手机上给某个 PC 端的朋友发送一个消息,经过无线网的 IEEE 802.1x 认证转到光纤通信上,然后进入内部企业网 IEEE 802.3,并最终到达目标 PC。由于不同硬件物理特性不同,对数据帧的最大长度有不同的限制,这个最大长度被称为最大传输单元,即 MTU。在不同的物理网之间就可能需要对 IP 报文进行分片,这个工作通常由路由器负责完成。

IP 是 TCP/IP 的基石,几乎所有其他协议都建立在 IP 所提供的服务基础上进行传输,包括在实际应用中用于传输稳定有序数据的 TCP。


TCP 建立连接

TCP 是一种面向连接、确保数据在端到端间可靠传输的协议。面向连接是指在发送数据前,需要先建立一条虚拟的链路,然后让数据在这条链路上"流动"完成传输。为了确保可靠性,不仅需要对发出的每一个字节进行编号确认,校验每一个数据包的有效性,在出现超时情况时进行重传,还需要通过实现滑动窗口和拥塞控制等级制,避免网络状况恶化而影响数据传输的极端情况。每个 TCP 数据包是封装在 IP 包中的,每一个 IP 头的后面紧跟着的是 TCP 头。

协议第一行的两个端口号各占 2 字节,分别代表了源机器和目标机器的端口号。这两个端口号与 IP 报头中的源 IP 地址和目标 IP 地址组成的四元组可以唯一标识一条 TCP 连接。由于 TCP 是面向连接的,因此有服务端和客户端之分,需要服务端先在相应的端口上进行监听,准备好接收客户端发起的建立连接请求。当客户端发起第一个请求建立连接的 TCP 包时,目标机器端口就是服务端所监听的端口号,HTTP 服务的 80 端口、HTTPS 服务的 443 端口、SSH 服务的 22端口等。可通过 netstat 命令列出机器上已建立的连接信息。

协议第二行和第三行时序列号,各占 4 字节。前者是指所发送数据包中数据部分第一个字节的序号,后者是期望收到来自对方的下一个数据包中数据部分第一个字节的序号。由于 TCP 报头中存在一些扩展字段,所以需要通过长度为 4 位的偏移字段表示 TCP 报头的大小,这样接收方才能准确地计算出包中数据部分的开始位置。

TCP 的 FLAG 由 6 位组成,分别代表 SYN、ACK、FIN、URG、PSH、RST,都以置 1 代表有效。SYN 用作建立连接时的同步信号;ACK 用于对收到的数据进行确认,所确认的数据由确认序列号表示;FIN 表示后面没有数据需要发送,通常意味着所建立的连接需要关闭了。

假设 A 机器是客户端角色,B 机器是服务端角色,三次握手指的是建立连接的三个步骤:

  • A 机器发出一个数据包并将 SYN 置 1,表示希望建立连接。这个包中的序号假设是 x。
  • B 机器收到 A 机器发送过来的数据包后,通过 SYN 得知这是一个建立连接的请求,于是发送一个响应包并将 SYN 和 ACK 都置 1。假设这个包中的序列号是 y,而确认序列号必须是 x+1,表示收到了 A 发送过来的 SYN。在 TCP 中,SYN 被当作数据部分的一个字节。
  • A 收到 B 的响应包后需进行确认,确认包中将 ACK 置 1,并将确认序列号设置为 y+1,表示收到了来自 B 的 SYN。

需要三次握手的原因:

主要有两个目的,信息对等和防止超时。从信息对等的角度看,双方只有确定 4 类信息才能建立连接。在第二次握手后,从 B 机器的角度看,还不能确定自己的发报能力和对方的收报能力,在第三次握手后才能确认。

第 N 次握手 A 确认自己发送 A 确认自己接收 A 确认 B 发送 A 确认 B 接收 B 确认自己发送 B 确认自己接收 B 确认 A 发送 B 确认 A 接收
1 N N N N N Y Y N
2 Y Y Y Y N Y Y N
3 Y Y Y Y Y Y Y Y

连接三次握手也是防止出现请求超时导致脏连接,TTL 网络报文的生存时间往往会超过 TCP 请求超时时间,如果两次握手可以建立连接,传输数据并释放连接后,第一个超时连接请求才到达 B 机器的话,B 机器会误以为是 A 创建新的连接请求,然后确认同意创建连接。因为 A 机器的状态不是 SYN_SENT,所以之间丢弃了 B 的确认数据,以致最后只是 B 机器单方面创建连接完毕。

如果是三次握手,则 B 机器收到连接请求后,同样会向 A 机器确认同意创建建立,但因为 A 机器不是 SYN_SENT 状态所以直接丢弃,B 机器由于长时间没有收到确认信息,最终超时导致创建连接失败,因此不会出现脏连接。

从编程的角度,TCP 连接的建立是通过文件描述符(File Descriptor,fd)完成的。通过创建套接字获得一个 fd,然后服务端和客户端需要基于所获得的 fd 调用不同的函数分别进入监听状态和发起连接请求。由于 fd 的数量将决定服务端进程所能建立连接的数量,对于大规模分布式服务,当 fd 不足时就会出现"open too many files"错误而使得无法建立更多链接。因此需要注意调整服务端进程和操作系统所支持的最大文件句柄数,通过使用 ulimit -n 命令查看单个进程可以打开文件句柄的数量,lsof 命令用于查看当前系统打开 fd 的数量。想知道具体的 PID 所对应的具体应用程序,可以使用 ps -ax|grep pid

TCP 在协议层面支持 KeepAlive 功能,即隔段时间通过向对方发送数据表示连接处于健康状态。不少服务将确保连接健康的行为放到了应用层,通过定期发送心跳包检查连接的健康度,一旦心跳包出现一场不仅会主动关闭连接,还会回收与连接相关的其他用于提供服务的资源,确保系统资源最大限度地被有效利用。


TCP 断开连接

TCP 是全双工通信,通信双方都能作为数据的发送方和接收方。A 机器想要关闭连接,则待本方数据发送完毕后,传递 FIN 信号给 B 机器。B 机器应答 ACK,告诉 A 机器可以断开,但是需要等 B 机器处理完数据再主动给 A 机器发送 FIN 信号,此时 TCP 处于半关闭状态,A 到 B 方向的连接已经关闭了。

B 机器做好连接关闭前的准备工作后,发送 FIN 给 A 机器,此时 B 机器进入 LAST_ACK 状态等待 A 的最后确认。A 机器发送针对 B 机器 FIN 的 ACK 后,进入 TIME_WAIT 状态,经过 2MSL 后没有收到 B 传来的报文,则确定 B 机器已经收到 A 机器最后发送的 ACK 指令,此时 TCP 连接正式释放。

TIME-WAIT 和 CLOSE_WAIT 分别表示主动关闭和被动关闭产生的阶段性状态,如果在线上服务器大量出现这两种状态,就会加重机器负载,也会影响有效连接的创建,因此需要进行针对性的调优。

  • TIME_WAIT:主动要求关闭的机器在收到了对方发出的 FIN 报文,并发送了 ACK 之后就进入 TIME_WAIT 状态,等待 2MSL 之后即可进入 CLOSE 状态,如果 FIN_WAIT_1 状态下同时受到了带 FIN 和 ACK 标志的报文可以直接进入 TIME_WAIT 而无需经过 FIN_WAIT_2 状态。
  • CLOSE_WAIT:被动要求关闭的机器在收到对方请求关闭连接的 FIN 报文并进行第一次 ACK 应答后马上进入 CLOSE_WAIT 状态。这种状态确实表示在等待关闭,并且通知应用程序发送剩余数据,处理现场信息,关闭相关资源。

在 TIME_WAIT 等待的 2MSL 是报文在网络上生存的最长时间,超过阈值报文则被丢弃。一般来说 MSL 大于 TTL 衰减至 0 的时间,在 RFC793 中规定 MSL 为 2 分钟,在当前网络下 2 分钟会造成极大的资源浪费,在高并发的情况下服务器通常会使用更小的值。之所以不直接关闭,而要等待 2MSL 主要有两个原因:

  • 第一,确认被动关闭方能够顺利进入 CLOSED 状态。假如最后一个 ACK 由于网络原因导致无法到达 B 机器,处于 LAST_ACK 的 B 机器通常会认为对方没有收到自己的 FIN+ACK 报文而进行超时重传,A 机器收到第二次的 FIN+ACK 报文,会重发一次 ACK 并重新计时。如果 A 机器收到 B 机器的 FIN+ACK 报文后发送一个 ACK 给 B 机器就立即进入 CLOSED 状态,可能会导致 B 机器无法确保收到最后的 ACK 指令,也无法进入 CLOSED 状态。
  • 第二,防止失效请求。这样做是为了防止已失效连接的请求数据包与正常连接的请求数据包混淆而发生异常。

因为 TIME_WAIT 状态无法真正释放句柄资源,在此期间 Socket 中使用的本地端口在默认情况下不能再被使用。该限制对于客户端来说没有影响,但是对于高并发服务器来说会极大地限制有效连接地创建数量,称为性能瓶颈。因此建议将高并发服务器的 TIME_WAIT 调小。在服务器上通过变更 /etc/sysctl.conf 文件来修改该值,建议小于 30 秒。修改完之后执行 /sbin/sysctl -p 让参数生效即可。

TIME_WAIT 是四次挥手断开连接的尾声,如果此状态连接过多则可以通过优化服务器参数得到解决。如果不是对方的连接异常,一般不会出现连接无法关闭的状态。但是 CLOSE_WAIT 过多很可能是程序自身的问题,比如在对方关闭连接后程序没有检测到,或者忘记自己关闭连接。


信息安全

黑客攻击主要分为非破坏性攻击和破坏性攻击。非破坏性攻击一般是为了扰乱系统的运行,使之暂时失去正常对外提供服务的能力,比如 DDos 攻击等。破坏性攻击主要会造成两种后果:系统数据受损或信息被窃取,比如 CSRF 攻击等。黑客攻击的手段有病毒式、洪水式、系统漏洞等。互联网需要建立一套完整的信息安全体系,遵循 CIA 原则,即保密性、完整性和可用性。

  • 保密性

    对需要保护的数据进行保密操作,无论是存储还是运输,都要保证用户数据及相关资源的安全。比如在存储文件时进行加密,在数据传输中也会通过各种编码方式对数据进行加密等。在实际编程中通常使用加密等手段保证数据的安全,黑客有可能是是内部的因此大多数企业的用户敏感信息都不是以明文存储的,避免数据管理员直接拖库下载。

  • 完整性

    访问的数据需要是完整的,而不是缺失的或者被篡改的,不然用户访问的数据就是不正确的。例如在商场看中了一个 A 型号的收集,但售货员在包装时被其他人换成了更便宜的型号为 B 的收集,这就是资源被替换了。在实际编写代码中,一定要确保数据的完整性,通常的做法是对数据进行签名和校验(例如 MD5 和数字签名等)。

  • 可用性

    服务需要是可用的,如果服务器都不可用自然也没有安全这一说了。例如去商场买东西,如果有人故意破坏,雇佣大量水军在商场收银台排队,既不结账也不走导致其他人无法付款,这就是服务不可用的表现。这个例子和常见的 DoS 攻击很相似,对于这种情况通常使用访问控制、限流、数据清洗等手段解决。


SQL 注入

SQL 注入是注入式攻击的常见类型,SQL 注入攻击是未将代码与数据进行严格隔离,导致在读取用户数据的时候错误地把数据作为代码地一部分执行,从而导致一些安全问题。典型的 SQL 注入例子是当对 SQL 语句进行字符串拼接操作时,直接使用未加转义的用户输入内容作为变量,例如:

var testCondition;
testCondition = Request.from("testCondition");
var sql = "select * from tableA where id ='" + testCondition + "'";

如果用户输入的 ID 是一个数字是没有问题的,可以执行正确的查询语句。但如果直接使用";"隔开,在 testCondition 中插入其他 SQL 语句,则会产生未知的结果,例如输入 drop、delete 等。

再例如当用户修改签名时,偶然输入 "# -- !#@ ,由于未对危险字符串 # -- 进行转义,导致 where 后边的信息被注释掉,执行语句变为:

update table set memo = "\"# -- !#@" where user_id = 123;	

该 SQL 语句的执行导致全库的 memo 字段都被更新,预防 SQL 注入主要从以下几方面考虑:

  • 过滤用户输入参数中的特殊字符,降低风险。
  • 禁止通过字符串拼接的 SQL 语句,严格使用参数绑定传入 SQL 参数。
  • 合理使用数据库访问框架提供的防注入机制,例如 MyBatis 提供的 #{} 绑定参数从而防止 SQL 注入,谨慎使用 ${},它相当于使用字符串拼接 SQL。

XSS

XSS 表示跨站脚本攻击,XSS 是指黑客通过技术手段,向正常用户请求的 HTML 页面中插入恶意脚本,从而可以执行任意脚本。XSS 主要分为反射型 XSS、存储型 XSS 和 DOM 型 XSS。XSS 主要用于信息窃取、破坏等目的。

反射型 XSS 示例:

<div>
<h3>反射型 XSS 示例</h3>
<br>用户:<%= request.getParameter("userName") %>
<br>系统错误信息:<%= request.getParameter("errorMessage") %>
</div>

以上代码从 HTTP 请求中取了 userName 和 errorMessage 两个参数并直接输出到 HTML 中展示,当黑客构建如下的 URL 时就出现了反射型 XSS,用户浏览器就可以执行客户的 JavaScript 脚本。

http://xss.demo/self-xss.jsp?userName= 张三 <script>alert("张三")</script>&errorMessage= XSS示例<script src= http://hacker.demo/xss-script.js/>

在防范 XSS 上,主要通过对用户输入数据做转义或者过滤。例如可以使用 Jsoup 框架对用户输入字符串做 XSS 过滤,或者使用框架提供的工具类对用户输入的字符串做 HTML 转义,例如 Spring 提供的 HtmlUtils。前端在浏览器展示数据时,也需要使用安全的 API,比如 innerText 而不是 innerHTML。


CSRF

CSRF 代表跨站请求伪造,即在用户不知情的情况下,冒充用户发起请求,在当前已经登录的 Web 应用程序上执行恶意操作,如恶意发帖、修改密码、发邮件等。

CSRF 有别于 XSS,从攻击效果上两者有重合的地方。从技术原理上两者的本质不同,XSS 是在正常用户请求的 HTML 页面中执行了黑客提供的恶意代码,CSRF 是黑客直接盗用用户浏览器中的登录信息,冒充用户去执行恶意操作。XSS 问题出现在用户数据没有过滤和转义,CSRF 问题出在 HTTP 接口没有防范不受信任的调用。

防范 CSRF 主要通过以下方式:

  • CSRF Token 验证,利用浏览器的同源限制,在 HTTP 接口执行前验证页面或 Cookie 中设置的 Token,只有验证通过才能继续执行请求。
  • 人机交互,例如调用网上银行转账接口时校验短信验证码。

HTTPS

HTTPS 即 HTTP over SSL,在之前的 HTTP 传输上增加了 SSL 协议的加密能力,SSL 是安全套接字层,SSL 协议工作于传输层与应用层直接,为应用提供数据加密的服务。之前使用的是 DES 对称加密算法,安全性不高,后来使用了 RSA 非对称加密算法,它把密码革命性地分成公钥和私钥,私钥是用来对公钥加密的信息进行解密的,需要严格保密。公钥对信息进行加密,任何人包括黑卡都可以知道。

非对称加密的安全性是基于大质数分解的困难,在非对称加密中公钥和私钥是一对大质数函数,计算两个大质数的乘积是简单的,但这个过程的逆运算非常困难。在 RSA 中解密出明文的难度等同于分解两个大质数的难度,因此在实际传输中,可以把公钥发送给对方,一方发送信息时,使用另一方的公钥进行加密生成密文,收到密文的另一方再用私钥进行解密,这样一来传输就相对安全了。

但非对称加密并不是完美的,它有一个明显的缺点就是加密和解密耗时长,只适合对少量数据进行处理。因此密钥的传输使用非对称加密,HTTPS 也是以这种方式来建立安全的 SSL 连接的,用户甲和乙进行非对称加密的传输过程如下:

  • 甲告诉乙,使用 RSA 算法进行加密,乙回答说好的。
  • 甲和乙分别根据 RSA 生成一对密钥,互相发送公钥。
  • 甲使用乙的公钥给乙加密报文信息。
  • 乙收到信息用自己的私钥解密。
  • 乙使用同样的方式给甲发送信息,甲使用同样方式解密。

这个过程还是存在安全问题的,例如在第二步,如果甲的信息被黑客拦截,虽然黑客无法破解加密信息,但是可以使用自己生成的密钥冒充甲的信息发送到乙。所以除了加密问题还有信任问题,这个问题通过 CA 解决,CA 就是颁发 HTTPS 证书的组织,HTTPS 是当前网站的主流文本传输协议,在基于 HTTPS 进行连接,就需要数字证书。

访问一个 HTTPS 的网站大致流程如下:

  • 浏览器向服务器发送请求,请求中包括浏览器支持的协议,并附带一个随机数。
  • 服务器收到请求后,选择某种非对称加密算法,把数字证书签名公钥、身份信息发送给浏览器,同时也附带一个随机数。
  • 浏览器收到后,验证证书的真实性,用服务器的公钥发送握手信息给服务器。
  • 服务器解密后,使用之前的随机数计算出一个对称加密的密钥,以此作为加密信息并发送。
  • 后续所有信息发送都是以对称加密的方式进行的。

在整个 HTTPS 的传输过程中,主要分为两部分:首先是 HTTPS 的握手,然后是数据的传输。前者是建立一个 HTTPS 的通道,并确定连接使用的加密套件及数据传输使用的密钥,而后者主要使用密钥对数据加密并传输。

HTTPS 建立连接的过程如下:

  • 客户端发送一个 Client Hello 协议的请求,在 Client Hello 中最重要的信息是 Cipher Suites,这里客户端会告诉服务器自己支持哪些加密的组件。
  • 服务端在收到客户端发送的 Client Hello 请求后,会返回一系列的协议数据,并以一个没有数据内容的 Server Hello Done 作为结束。这些协议数据有的是单独发送,有的则是合并发送。其中的重要协议包括:
    • Server Hello:告知客户端后续协议中要使用的 TLS 协议版本,还会确认后续采用的加密套件。
    • Certificate:主要传输服务端的证书内容。
    • Server Key Exchange:如果在 Certificate 协议中未给出客户端足够的信息,例如公钥信息,则会在该协议进行补充。
    • Certificate Request:这个协议是一个可选项,当服务端需要对客户端进行证书认证的时候,才会向客户端发送一个证书请求。
  • 客户端在收到服务器的握手信息后,根据服务器的请求,也会发送一系列协议。
    • Certificate:可选项,假如服务端发送了 Certificate Request 需要对客户端进行证书认证,客户端就需要发送自己的证书信息。
    • Client Key Exchange:与 Server Key Exchange 类似,是对客户端 Certificate 信息的补充,例如补充客户端证书的公钥信息。
    • Certification Verity:对服务端发送的证书进行进行确认。
    • Change Cipher Spec:该协议作为一个独立协议告诉服务端,客户端已经接收之前服务端确认的加密套件,并会在后续通信中使用该加密套件进行加密。
    • Encrypted Handshake Message:用于客户端给服务端加密套件加密一段 Finish 数据,验证建立起来的加解密通道的正确性。
  • 服务端在接收客户端的确认信息以及验证信息后,会对客户端发送的数据进行确认。
    • Change Cipher Spec:通过使用私钥对客户端发送的数据进行解密,并告知后续将使用协商好的加密套件进行加密。
    • Encrypted Handshake Message:与客户端的操作相同,发送一段 Finish 数据验证通道的正确性。
  • 客户端和服务端都确认加解密无误后,按照之前约定的 Session Secret 对应用数据进行加密传输。

猜你喜欢

转载自blog.csdn.net/qq_41112238/article/details/106904889