美文网首页
http全解

http全解

作者: 彳亍cium | 来源:发表于2019-10-07 18:28 被阅读0次

    http是目前访问网页最主流的应用层协议,但是对于http应用层和下面的传输层之间的关系,一直不是特别的清楚,遂就本文对http协议从网络层次的角度来仔细剖析,并写了python脚本试图复原整个的http请求全流程。

    1. 用scapy实现http请求全流程

    1.1 http请求和响应的全流程

    1562218975750.jpg

    主要的步骤包括建立连接、http request和response交互、关闭连接等,下面逐一讲解。

    1.2 TCP/IP协议

    TCP/IP协议模型(Transmission Control Protocol/Internet Protocol),包含了一系列构成互联网基础的网络协议,是Internet的核心协议,通过20多年的发展已日渐成熟,并被广泛应用于局域网和广域网中(如果局域网是小溪,很多条小溪就汇聚成大的干流,比如长江、黄河,干流就是广域网,然后再流入大海,大海就是互联网),目前已成为事实上的国际标准。TCP/IP协议簇是一组不同层次上的多个协议的组合,通常被认为是一个四层协议系统,与OSI的七层模型相对应。

    1562219004363.jpg

    HTTP协议就是基于TCP/IP协议模型来传输信息的,http协议是无状态,面向连接的,位于应用层的协议。下面的思维导图是tcp/ip涉及到的知识框架。

    1562219014914.png

    (1). 链路层

    也称作数据链路层或网络接口层(在第一个图中为网络接口层和硬件层),通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡。它们一起处理与电缆(或其他任何传输媒介)的物理接口细节。ARP(地址解析协议)和RARP(逆地址解析协议)是某些网络接口(如以太网和令牌环网)使用的特殊协议,用来转换IP层和网络接口层使用的地址。

    (2). 网络层

    也称作互联网层(在第一个图中为网际层),处理分组在网络中的活动,例如分组的选路。在TCP/IP协议族中,网络层协议包括IP协议(网际协议),ICMP协议(Internet互联网控制报文协议),以及IGMP协议(Internet组管理协议)。

    IP是一种网络层协议,提供的是一种不可靠的服务,它只是尽可能快地把分组从源结点送到目的结点,但是并不提供任何可靠性保证。同时被TCP和UDP使用。TCP和UDP的每组数据都通过端系统和每个中间路由器中的IP层在互联网中进行传输。

    ICMP是IP协议的附属协议。IP层用它来与其他主机或路由器交换错误报文和其他重要信息。

    IGMP是Internet组管理协议。它用来把一个UDP数据报多播到多个主机。

    (3). 传输层

    主要为两台主机上的应用程序提供端到端的通信。在TCP/IP协议族中,有两个互不相同的传输协议:TCP(传输控制协议)和UDP(用户数据报协议)。

    TCP为两台主机提供高可靠性的数据通信。它所做的工作包括把应用程序交给它的数据分成合适的小块交给下面的网络层,确认接收到的分组,设置发送最后确认分组的超时时钟等。由于运输层提供了高可靠性的端到端的通信,因此应用层可以忽略所有这些细节。为了提供可靠的服务,TCP采用了超时重传、发送和接收端到端的确认分组等机制。

    UDP则为应用层提供一种非常简单的服务。它只是把称作数据报的分组从一台主机发送到另一台主机,但并不保证该数据报能到达另一端。一个数据报是指从发送方传输到接收方的一个信息单元(例如,发送方指定的一定字节数的信息)。UDP协议任何必需的可靠性必须由应用层来提供。

    (4). 应用层

    应用层决定了向用户提供应用服务时通信的活动。TCP/IP 协议族内预存了各类通用的应用服务。包括 HTTP,FTP(File Transfer Protocol,文件传输协议),DNS(Domain Name System,域名系统)服务。

    数据包进入到每一个层,都会被加上相应的头部,各层的协议就是数据包头部的规范,是统一的数据包结构说明。

    • 包可以说是全能性术语;

    • 帧用于表示数据链路层中包的单位;

    • 数据包是 IP 和 UDP 等网络层以上的分层中包的单位;

    • 段则表示 TCP 数据流中的信息;

    • 消息是指应用协议中数据的单位

    1562219026590.jpg

    从TCP/IP模型的角度来理解了一次HTTP请求与响应的过程

    1562219035899.jpg

    下面再看上面的的;流程是如何一步一步完成的,各个层分别做了什么作用。

    1.3 TCP三次握手

    HTTP基本都是基于TCP的,TCP是面向连接的,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。在TCP/IP协议中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的。三次握手的目的是同步连接双方的序列号和确认号并交换 TCP窗口大小信息。

    1562219042615.jpg
    • 第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;

    • 第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;

    • 第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。

    为什么要三次握手

    为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误:

    具体例子:“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”

    1.4 HTTP协议

    Http是什么?

    通俗来讲,他就是计算机通过网络进行通信的规则,是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据。目前任何终端(手机,笔记本电脑。。)之间进行任何一种通信都必须按照Http协议进行,否则无法连接。

    四个基于:

    请求与响应: 客户端发送请求,服务器端响应数据

    无状态的: 协议对于事务处理没有记忆能力,客户端第一次与服务器建立连接发送请求时需要进行一系列的安全认证匹配等,因此增加页面等待时间,当客户端向服务器端发送请求,服务器端响应完毕后,两者断开连接,也不保存连接状态,一刀两断!恩断义绝!从此路人!下一次客户端向同样的服务器发送请求时,由于他们之前已经遗忘了彼此,所以需要重新建立连接。

    应用层: Http是属于应用层的协议,配合TCP/IP使用。

    TCP/IP: Http使用TCP作为它的支撑运输协议。HTTP客户机发起一个与服务器的TCP连接,一旦连接建立,浏览器(客户机)和服务器进程就可以通过套接字接口访问TCP。

    针对无状态的一些解决策略:

    有时需要对用户之前的HTTP通信状态进行保存,比如执行一次登陆操作,在30分钟内所有的请求都不需要再次登陆。于是引入了Cookie技术。

    HTTP/1.1想出了持久连接(HTTP keep-alive)方法。其特点是,只要任意一端没有明确提出断开连接,则保持TCP连接状态,在请求首部字段中的Connection: keep-alive即为表明使用了持久连接。
    等等还有很多。。。。。。

    下面开始讲解重头戏:HTTP请求报文,响应报文,对应于上述步骤的2,3,4,5,6。

    HTTP报文是面向文本的,报文中的每一个字段都是一些ASCII码串,各个字段的长度是不确定的。HTTP有两类报文:请求报文和响应报文。

    HTTP请求报文

    一个HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成,下图给出了请求报文的一般格式。

    1562219058948.jpg

    请求行分为三个部分:请求方法、请求地址和协议版本

    1. 请求方法

    HTTP/1.1 定义的请求方法有8种:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE

    2.请求头部

    请求头部为请求报文添加了一些附加信息,由“名/值”对组成,每行一对,名和值之间使用冒号分隔。

    1562219067839.jpg

    下面是http response包的一些结构具体的可以参照下面的实例

    1562219074748.jpg
    http response的结构实例
    HTTP/1.1 200 OK  状态行
    
    Date: Sun, 17 Mar 2017 08:12:54 GMT  响应头部
    
    Server: Apache/2.2.8 (Win32) PHP/5.2.5
    
    X-Powered-By: PHP/5.2.5
    
    Set-Cookie: PHPSESSID=c0huq7pdkmm5gg6osoe3mgjmm3; path=/
    
    Expires: Thu, 19 Nov 1981 08:52:00 GMT
    
    Cache-Control: no-store, no-cache, must-revalidate, post-check=0,pre-check=0
    
    Pragma: no-cache
    
    Content-Length: 4393
    
    Keep-Alive: timeout=5, max=100
    
    Connection: Keep-Alive
    
    Content-Type: text/html; charset=utf-8
    
    空行
    
    <html>  响应数据
    
    <head>
    
    <title>HTTP响应示例<title>
    
    </head>
    
    <body>
    
    Hello HTTP!
    
    </body>
    
    </html>
    

    下面是python脚本,利用scapy模块库,生成数据包,建立tcp三次握手,传输http报文的代码

    #! /usr/bin/env python
    # Change log level to suppress annoying IPv6 error
    import logging
    logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
    
    # Import scapy
    from scapy.all import *
    
    # Print info header
    print "[*] ACK-GET example -- Thijs 'Thice' Bosschert, 06-06-2011"
    
    # Prepare GET statement
    get = 'GET / HTTP/1.1\r\nHost:49.234.82.163\r\nUser-Agent:python/2.7\r\nAccept:*/*\r\n\r\n'
    # Set up target IP
    ip=IP(dst="49.234.82.163")
    
    # Generate random source port number
    port=RandNum(1024,65535)
    
    # Create SYN packet
    SYN=ip/TCP(sport=port, dport=80, flags="S", seq=42)
    
    # Send SYN and receive SYN,ACK
    print "\n[*] Sending SYN packet"
    SYNACK=sr1(SYN)
    
    # Create ACK with GET request
    ACK=ip/TCP(sport=SYNACK.dport, dport=80, flags="A", seq=SYNACK.ack,
    ack=SYNACK.seq + 1) 
    
    # SEND our ACK-GET request
    print "\n[*] Sending ACK-GET packet"
    send(ACK)
    
    reply,error=sr(ip/TCP(sport=SYNACK.dport, dport=80, flags="PA", seq=SYNACK.ack,ack=SYNACK.seq + 1)/get)
    
    
    # print reply from server
    print "\n[*] Reply from server:"
    print reply.show()
    print reply[0]
    
    print '\n[*] Done!'
    

    可以用抓包的wireshark发现服务器端有回复ok,也可以用sniff()来嗅探,

    a=sniff(iface ="enp0s8", filter = "tcp",prn=lambda x: x.show2())

    其中iface因电脑而异的,然后可以看到如下所示,具体的是哪一个索引也跟目前电脑运行的状态有关,可以多输出几个试试

    但是握手过程可能会因为tcp的RST而导致握手失败,只主要是linux内核收到ack时,认为自己并没有发出SYN,所以发出RST,重置连接,需要配置iptable过滤掉RST的包,下面的IP为clinet端的ip
    iptables -A OUTPUT -p tcp --tcp-flags RST RST -s 192.168.1.20 -j DROP

    1562219087505.png

    2. http/url & p4存在的问题

    • http面向连接的,是需要占用端口的,如果实现的话,需要实现tcp层的功能
    • http的request和response不是一一对应关系,而且他们的对应序列也可以按照网络不同而不同,这由传输层来帮忙完成,需要找到对应关系???从抓包的结果来看:一对会话的ack、seq和tcp length有关的即ack = seq + tcp_length;其中seq就等于上一次发过来的ack或者随机也可以
    • http的response可以是单个数据包,也可以是多个,可以考虑单个的小的响应包

    相关文章

      网友评论

          本文标题:http全解

          本文链接:https://www.haomeiwen.com/subject/ilimpctx.html