由于现在的业务系统通常提供 Web 服务,因此 Web 安全成为了企业/单位网络安全中的重要部分。这篇文档记录了对 HTTP 与 HTTPS 的学习笔记。由于 HTTPS 仅仅只是再数据传输过程中套了一个 TLS 的壳子,因此也放到这篇文档中。关于 TLS 的学习会另写一篇文档。
Term
- HTTP (Hyper Text Transfer Protocol over Secure Socket Layer, 超文本传输协议),
- HTTPS (Hyper Text Transfer Protocol Secure, 超文本传输安全协议):是以安全为目标的 HTTP 通道,在 HTTP 协议的基础上,在传输层使用 TLS 对通信过程进行加密。简单讲是 HTTP 的安全版。
- RFC (Request for Comments, 请求注解):是 IETF 发布的一系列技术文档,定义了互联网的核心协议与标准。
- TLS (Transport Layer Security, 传输层安全协议):用于在两个通信应用程序之间提供保密性和数据完整性。该协议由两层组成: TLS 记录协议 (TLS Record) 和 TLS 握手协议 (TLS Handshake),是更新、更安全的 SSL 版本。
- SSL (Secure Sockets Layer, 安全套接层协议):
- IETF (Internet Engineering Task Force, Internet 工作小组):
- URL (uniform resource locator, 统一资源定位系统):
- W3C (World Wide Web Consortium, 万维网联盟):
HTTP 协议简介
HTTP (Hyper Text Transfer Protocol, 超文本传输协议) 的发展是 W3C 和 IETF 合作的结果,他们最终发布了一系列的 RFC,RFC 1945 定义了 HTTP/1.0 版本。其中最著名的就是 RFC 2616,它定义了今天普遍使用的一个版本,HTTP 1.1。
HTTP 是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型 (C/S)。是一个无状态的协议。用于从 WWW 服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。
**HTTP 和 HTTPS:HTTP 协议和 HTTPS 协议都是一种发布和接收 HTML 页面的方法。两者之间的区别在于 HTTPS 是 HTTP 的安全版,在 HTTP 下加入 SSL/TLS 层。HTTP 和 HTTPS 均是由 TCP 协议封装而来,在进行 HTTP 协议和 HTTPS 协议时,需要进行三次握手和四次挥手。HTTP 使用 80 端口,HTTPS 使用 443 端口。
HTTP 数据报文
客户端与服务器端之间的通信,通过 HTTP 协议,以 HTTP 报文的形式来实现数据的交互。HTTP 报文是 HTTP 通信时发送的数据块,由三部分组成:状态行(请求行 | 响应行)、首部、主体。也有些书籍说是由首部和主体两部分组成,状态行包含在首部中,但绝大多数的说法是由三部分组成。HTTP报文可以分为请求报文和响应报文;请求报文向服务器传达请求,响应报文将请求的结果返回给客户端。大致上,HTTP 数据报文的结构如下:
+-----------------------+
| Request / Status Line |
+-----------------------+
| Header |
+-----------------------+
| (Blank Line) |
+-----------------------+
| Payuload-Body |
+-----------------------+
HTTP 报文以状态行开始,跟在后面的是 HTTP 首部。首部由多个首部字段构成,每行一个首部字段;HTTP 首部后是一个空行,然后是报文主体。
可以看到,状态行和首部中的每行都是以回车符(\r,%0d,CR)和换行符(\n,%0a,LF)结束,这是因为 HTTP 规范中行应该使用 CRLF 结束。另外,首部和主体之间由一空行隔开,或者可以理解为 HTTP 首部的最后一个字段有两个 CRLF。与状态行和首部不同的是,主体是可选的,也就是说报文中不一定要有主体;另外状态行和首部是 ASCII 文本,主体可包含文本或二进制数据(如图片、视频、软件等)。
以上就是 HTTP 报文的大概结构,下面直观地展示一下报文格式:
请求报文结构
要注意的一点就是,即使请求报文的 body 部分是空的,请求头部后的回车换行符也是必须要有的。
GET /test/hi-there.txt HTTP/1.0
Accept: text/*
Accept-Language: em,fr
Host:www.joes-hardware.com
HTTP报文以状态行开始,请求报文中的状态行叫请求行。
请求行 (GET /test/hi-there.txt HTTP/1.0):由请求方法、URL、协议版本组成,这些字段都由空格分隔。请求行表明了要对哪个资源执行哪个方法,具体有哪些请求方法,文章后面会详细介绍。
GET:请求方法/test/hi-there.txt:URL,表示要获取的资源位于服务器的/test/hi-there.txt路径下HTTP/1.0:HTTP 协议的版本,这里使用的是 HTTPv1
首部:HTTP 首部由多个首部字段构成,旨在向报文中添加一些通信过程中所需的重要信息。
Accept: text/*:Accept-Language: em,fr:Host:www.joes-hardware.com:
这个请求报文不包含请求主体。因此空白行之后没有内容。
响应报文结构
HTTP/1.0 200 OK
Content-type: text/plain
Content-length: 19
Hi! i'm a message!
HTTP 响应报文中的状态行叫响应行。响应行由协议版本、状态码、原因短语(状态码描述)组成。这些字段同样都由空格分隔。响应行表明了服务器对请求的处理结果,由状态码体现。
-
HTTP/1.0:HTTP 协议的版本 -
200:状态码。HTTP 协议将状态码分成了 5 类,在下面的章节中会详细介绍。 -
OK:原因短语。值得注意的是,原因短语是数字状态码的可读版本,描述数字状态码的含义,便于人理解,只对人有意义,因此以下两种响应行都会被当作成功处理。HTTP/1.0 200 NOT OK HTTP/1.0 200 OK
首部:HTTP 首部由多个首部字段构成,旨在向报文中添加一些通信过程中所需的重要信息。
Content-type: text/plain:Content-length: 19:
主体:报文主体包含了HTTP所要传输的内容。这里的主体数据为 Hi! i'm a message!。也是请求体 URL /test/hi-there.txt 所对应的资源。
HTTP 版本号
请求行和响应行中都包含 HTTP 版本号,其格式为:
HTTP/<major>.<minor>
major是主版本号,minor是次版本号,使用版本号的目的是规范双方之间通信的格式。
HTTP 请求方法
状态行部分讲到请求行中包含请求方法字段,请求方法告诉服务器要做什么。下图是HTTP规范中目前已定义的方法,绿色的是比较常用的方法。
首先,方法名称是区分大小写的。当你使用 get 作为请求方法去访问时,响应体状态码为 501(Not Implemented),因为服务器不认识这个请求方法。另外,服务器可以指定某个资源所支持的请求方法。如果贸然混用请求方法,可能导致 405(Method Not Allowed)。
作为最常用的两种请求方法,HTTP 服务器至少应该实现对 GET 和 HEAD 方法的支持,其他方法都是可选的。当然,所有的方法支持的实现都应当匹配下述的方法各自的语义定义。
首先简要的介绍常用的几种请求方法:
GET 用于请求获取实体资源,没有正文,提交的数据在 URL 的查询字符串中,以 ? 分割 URL 和传输数据,参数之间以 & 相连,如 EditBook?name=test1&id=123456.(请求头里面那个 content-type 做的这种参数形式,后面讲)。但提交的数据有长度限制(主要因为浏览器对 URL 长度有限制),且不安全。
HEAD 与 GET 类似,但是响应数据不要正文,只要头部。
POST 用于向服务器提交表单数据,有正文,且提交的数据在正文中,因此没有长度限制(甚至不限于 ASCII 字符)。
请求方法的属性
| 请求方法 | 说明 | HTTP版本 | RFC | Request payload body | Response payload body | 安全的 | 幂等的 | 可缓存的 |
|---|---|---|---|---|---|---|---|---|
| GET | 获取资源 | 1.0、1.1 | RFC 9110 | 可选 | 有 | 是 | 是 | 是 |
| HEAD | 获得报文首部 | 1.0、1.1 | RFC 9110 | 可选 | 有 | 是 | 是 | 是 |
| POST | 传输实体主体 | 1.0、1.1 | RFC 9110 | 有 | 有 | 否 | 否 | 是 |
| PUT | 传输文件 | 1.0、1.1 | RFC 9110 | 有 | 有 | 否 | 是 | 否 |
| DELETE | 删除文件 | 1.0、1.1 | RFC 9110 | 可选 | 有 | 否 | 是 | 否 |
| CONNECT | 要求用隧道协议连接代理 | 1.1 | RFC 9110 | 可选 | 有 | 否 | 否 | 否 |
| OPTIONS | 询问支持的方法 | 1.1 | RFC 9110 | 可选 | 有 | 是 | 是 | 否 |
| TRACE | 追踪路径 | 1.1 | RFC 9110 | 无 | 有 | 是 | 是 | 否 |
GET 方法
获取/查询资源的方法。是最常用的 HTTP 方法,常用于请求 URL 指定的资源,服务端经过处理将资源返回给客户端。比如访问百度首页,请求包如下,百度的服务器收到请求后,将百度首页返回给浏览器。
Request
GET / HTTP/1.1
Host: www.baidu.com
Connection: close
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept:text/html, application/xhtml+xml, application/xml; q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip,deflate
Accept-Language: zh-CN, zh; q=0.9
Cookie: BAIDUID=3EB9DFOBEFF98AASCC37B1DA4866644F:FG=1; BIDUPSID=3EBSDFOBBFF98AASCC37B1DA4866644F; PSTM=1531984106;_cfduid=d6caa21eb35d80a3e61cdea5ca03e80231533714714; BD_UPN=12314753; ispeed_lsm=2; BDORZ=B490ESEBF6F3CD402E515D22BCDA1598; delPer=0; BD_CK_SAM=1; PSINO=6; H _PS_PSSID=26524_1444_21114_27401_27376_26350; H_PS_645EC=521311xfITt632BihMECE2DBv67E3%2Bid7J2qBOKsVXXK2BcIC1LLHr6dLancPDtI; BDRCVFR[dG2JNJb_ajR]=mk3SLVN4 HKm; userFrom=www.baidu.com; BDRCVFR[-pGxjrCMryR]=mk3SLVN4HKm; BD_HOME=0
有时请求会传递一些参数给服务器,在 GET 方法中,这些参数会被包含在 URL 中,放在文件路径后面,用 ? 分隔,被称为查询字符串。查询字符串以键值对的形式存在,每个参数的键和值用 = 连接,不同参数之间用 & 符号连接。(详情请看 URL 格式)
百度搜索,对应的 URL 为:https://www.baidu.com/s?wd=google&tn=52176495_dg&ch=3&ie=utf-8。可以看出,google 被当成了 wd (word) 参数的参数值,放在请求行的 URL 字段中。抓包结果如下:
Request
GET /baidu?&ie=utf-s&word=google HTTP/1.1
Host: www.baidu.com
Connection: close
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/63.0.3239.132 Safari/537.36
Accept:text/html, application/xhtml+xml, application/xml; q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN, zh; q=0.9
Cookie: BAIDUID=3EB9DFOBBFF98AA8CC37E1DA1866644F:FG=1;BIDUPSID=3EESDFOEBFF98AASCC3TB1DA4866644F; PSTM=1531984106;_cfduid=dcaa21eb35d80a3e61cdea5ca03e80231533714714; BD_UPN=12314753;ispeed_lsm=2;BDORZ=B490B5EBFGF3CD402E515D22BCDA1598; H_PS_PSSID=26524_144421114_27401_27376_26350;delPer=0; BD_CK_SAM=1; BD_HOME=0; rsv_jmp_slow=1542165083809; PSINO=3;H_PS_645EC=05a2w00cxE6HWIKTGHW590yrQ7hIpMwPTL6hcxoTHRlbp596jUQyysDdlik; BDSVRIM=0
特意将请求报文全选,大家可以看到,首部字段下方有一空行,然后空行下面有一光标,这再次体现了 HTTP 报文的结构,也告诉大家,GET 请求是不包含请求主体的。
POST 方法
传输实体主体的方法。常用于向指定资源发送数据,指定的资源会对数据进行处理,然后将处理结果返回给客户端。数据被包含在请求主体中,一般用于表单提交、文件上传等。
Request
POST /index.php?c=login&m=tologin HTTP/1.1
Host: xxx.xxx.xxx
Content-Length: 48
Accept: application/json, text/javascript,*/*;q=0.01
Origin: <略>
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOWG4) AppleWebKit/537.36(KHTML,like Gecko) Chrome/63.0.3239.132_Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: <略>
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh; q=0.9
Cookie: <略>
Connection: close
name=root&pwd=root&code=eanwt&saveusername=false
以上是登录时的请求包,请求主体中包含了用户名、密码、验证码、是否记住用户名的参数数据,服务器接收到请求后,会交给 index.php 文件去处理,然后会返回一个处理结果,可能是登录失败,也可能是跳转到系统内部。可以看到,这些数据的格式和查询字符串的格式一致,Content-Type 字段值为 application/x-www-form-urlencoded,这是 POST 提交数据的几种格式之一,POST 提交数据的几种格式会在以后的文章中介绍。
当然,GET 方法也可以用来传输数据,但是首先 URL 的长度受浏览器、服务器、操作系统影响,其次是 GET 方法提交的参数都会在地址栏中显示出来,不安全,因此涉及到大量数据、敏感数据的时候,一般采用 POST 方法。
HEAD 方法
获取报文首部的方法。HEAD 方法和 GET 方法很像,但服务器接收到 HEAD 请求时,在响应中只会返回报文首部,不会返回报文主体。常用于测试请求资源是否存在或是否被修改。
我们再来看一下 GET 请求方法的响应体格式。
Request 01
GET / HTTP/1.1
Host :www.baidu.com
Connection: close
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept: text/html, application/xhtml+xml, application/xml; q=0.9, image/webp, image/apng, */*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Langaage: zh-CN,zh; q=0.9
Cockie: BAIBUID=3EB9DF0BBFF98AA8CC37B1DA4866644F:FG=1;BIDUPSID=3EB9DF0BBFF98AA8CC37B1DA4866644F; PSIM=1531984106; __cfduid=d6caa21eb35d80a3e61cdea5ca03e80231533714714; BD_UPN=12314753; ispeed_lsm=2
BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; delPer=O; BD_CK_SAN=1; PSINO=6; H_PS_PSSID=26524_1444_21114_27401_27376_26350;
H_PS_645EC=52131lxfHt6%2BihMEcEkOBv67E3%2Bid7J2qB0KsVXX%2BcTClULwr6dLancPDtI; BDRCVFR[dG2JNUJb_ajR]=mk3SLVN4HKm; userFrom=www.baidu.com; BDRCVFR[-pGxjrCMryR]=mk3SLVN4HKm
Response 01
HTTP/1.1 200 OK
Bdpagetype: 1
Bdqid: Ox8c0d593c0007ce95
Cache-Control: private
Content-Type: text/html
Cxy_all:. baidu+5c6559c8b999bl73a4ef66f7a285a3fa
Date: wed, 14 Nov 2018 08:31:55 GMT
Expires: wed.14 Nov 2018 08:31:04 CMT
Server: BWS/1.1
Set-Cookie: delPer=0; path=/; domain=.baida.com
Set-Cookie: BDSVRIM=0; path=/
Set-Cookie: BD_HOME=O; path=/
Set-Ccokie: H_PS_PSSID=26524_1444_21114_27401_27376_26350; path=/; domain=.baida.com
Strict-Transport-Security: max-age=172800
Vary: Accept-Eneoding
X-Ua-Compatible: IE=Edge, chrome=1
Connection: close
Content-Length: 121509
<!DOCTYPE html>
<!--STATUS OK-->
GET 请求,除了返回报文首部,还返回了主体,经浏览器解析,成为我们眼中的百度首部。而 HEAD 请求方法的响应体只包含状态行和 HEADER 部分。
Request 02
HEAD / HTTP/1.1
Host: www.baidu.com
Connection: close
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept: text/html, application/xaml+xml,application/xml; q=0.9,image/webp, image/apng, */*; q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN, zh; q=0.9
Cookie: BAIDUID=3EB9DFOBBFF98AA8CC37B1DA4866644F:FG=1; BIDUPSID=3EB9DF0BBFF98AA8CC37B1DA4866644F; psim=1531984106; __cfduid=d6caa21eb35d89a3e61cdea5ca03e80231533714714; BD_UPN=12314753; ispeed_lsm=2; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; delPer=0; abd_ck_sam=1; PSINO=6; H_PS_PSSID=26524_1444_21144_27376_26350; H_PS_645EC=52131lxfHt6%2BihMEcEk0Bc67E3%2Bid7J2qB0KsVXX%2BcTClULwr6dLancPDtI; BDRCVFR[dG2JNJb_ajR]=mk3SLVN4HKm; userFrom=www.baidu.com; BDRCVFR[-pGxjrCMryR]=mk3SLVN4HKm
Response 02
HTTP/1.1 200 OK
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Content-Type: text/html
Date: Wed,14 Nov 2018 08.32.51 GMT
Last-Modified: Mon, 13 Jun 2016 02:50:37 GMT
Pragma: no-cache
Server: bfe/1.0.8.18
Connection: close
HEAD 请求,只返回首部,没有主体。
OPTIONS 方法
查询资源支持的方法。用于查询 URL 指定的资源支持哪些方法,资源支持哪些方法,会在响应包的 Allow 字段中显示。
Request
OPTIONS / HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/xaml+xml, application/x-ms-xbap,*/*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.OE; Tablet PC 2.0)
Accept-Encoding: gzip, deflate
Host: <略>
Pragma: no-cache
Cookie: <略>
Connection: close
Response
HTTP/1.1 200 OK
Allow: OPTIONS, TRACE, GET, HEAD, POST
Server: Microsoft-IIS/7.5
Public: OPTIONS, TRACE, GET, HEAD, POST
X-Powered-By: ASP.NET
Date: Wed, 14 Nov 2018 09:44:51 GMT
Connection: close
Content-Length: 0
PUT 方法
上传文件的方法。服务器会将请求主体的内容保存到 URL 指定的资源位置,包含两种情况,(1) URL 指定的资源不存在;(2) URL 指定的资源存在。
如果 URL 指定的资源不存在,服务器会新建一个文件,将请求主体中的内容保存到新建的文件里,响应码为 201。
Request 01
PUT /test.html HTTP/1.1
Accept: text/html, application/xhtml+xml, imagel jxr, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 10.0;WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 192.168.208.130:8080
Pragma: no-cache
Connection: close
Content-Length: 20
this is a test page!
Response 01
HTTP/1.1 201 Created
Content-Length:0
Date: Thu, 15 Nov 2018 03:27:21 GMT
Connection: close
如果 URL 指定的资源存在,服务器会重置文件内容,用请求主体中的内容覆盖原文件内容,响应码为 200 或 204。
Request 02
PUT /test.html HTTP/1.1
Accept: text/html, application/xhtml+xml, imagel jxr, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 10.0;WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 192.168.208.130:8080
Pragma: no-cache
Connection: close
Content-Length: 20
this is a test page!
Response 02
HTTP/1.1 204 No Content
Date: Thu, 15 Nov 2018 08:27:41 GMT
Connection: close
需要注意的是,PUT 方法自身不带验证机制,任何人都可以执行,存在安全问题,所以网站一般不会使用 PUT 方法。
DELETE 方法
删除文件的方法。删除 URL 指定的资源,和 PUT 相反。
文件删除成功,响应码为 204。
Request 01
DELETE /test.html HTTP/1.1
Accept: text/html, application/ xhtml+xml, image/jxr, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 10.0: WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host : 192.168.208.130;8080
Pragma: no-cache
Connection: close
Content-Length: 0
Response 01
HTTP/1.1 204 No Content
Date: Thu, 15 Nov 2018 08:35:48 GMT
Connection: close
若删除的文件不存在,响应码为 404。
Request 02
DELETE /test.html HTTP/1.1
Accept: text/html, application/xhtml_xml, image/jxr, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host:192.168.208.130:8080
Pragma: no-cache
Connection: close
Content-Length: 0
Response 02
HTTP/1.1 404 Not Found
Content-Type: text/html:charset=utf-8
Content-Language: en
Content-Length:1047
Date: Thu, 15 Nov 2018 08:37:47 GMT
Connection: close
<!doctype html><html lang="en"><head><title>HTTP Status 404 - Not Found</title><style type="text/css">h1
{fout-family:tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;}h2
... ...
和 PUT 一样,DELETE 方法同样不带验证机制,所以网站一般也不使用 DELETE 方法。
TRACE 方法
路径追踪的方法。主要用于诊断,让服务器将收到的请求放在响应主体中,环回给客户端,这样客户端就可以判断发出的请求是否被请求/响应链(在客户端和服务器端之间,请求可能会经过代理、网关、防火墙等应用程序)篡改。
TRACE 请求不能带有实体的主体部分,TRACE 响应的实体主体包含服务器收到的请求。
Request 01
TRACE / HTTP/1.1
Accept: image/gif,image.jpeg,image/pjpeg,application/x-ms-application,application/xaml+xml,application/x-ms-xbap, */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible: MSIE 7.0; Windows NT 10.0; WOW64: Trident/7.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)
Accept-Encoding: gzip, deflate
Host:xxx.xxx.xxx
Pragma: no-cache
Connection: close
请求数据包被作为响应体返回,通过与原始请求数据包的对比,可以判断请求是否遭受篡改。
Response 01
HTTP/1.1 200 OK
Date: Thu, 15 Nov 2018 09:11:58 GMT
Server: Apache/2.2.23 (Unix) mod_jk/1.2.14
Connection: close
Content-Type: message/http
Content-Length: 378
TRACE / HTTP/1.1
Accept: image/gif,image.jpeg,image/pjpeg,application/x-ms-application,application/xaml+xml,application/x-ms-xbap, */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible: MSIE 7.0; Windows NT 10.0; WOW64: Trident/7.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)
Accept-Encoding: gzip, deflate
Host:xxx.xxx.xxx
Pragma: no-cache
Connection: close
以上都是方法没被禁用时的响应,如果方法被禁用,响应码为 405。
Request 02
PUT /test.html HTTP/1.1
Accept: image/gif,image.jpeg,image/pjpeg,application,application/xaml+xml,application/x-ms-xbap, */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible: MSIE 7.0; Windows NT 10.0; WOW64: Trident/7.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)
Accept-Encoding: gzip, deflate
Host:xxx.xxx.xxx
Pragma: no-cache
Connection: close
Content-Length: 19
this is a test page!
Response 02
HTTP/1.1 405 Method Not Allowed
Date: Thu, 15 Nov 2018 09:14:23 GMT
Server: Apache/2.2.23 (Unix) mod_jk/1.2.14
Allow: GET,HESD,POST,OPTIONS,TRACE
Content-Length: 230
Connection: close
Content-Type: text/html: charset-iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>405 Method Not Allowed</title>
</head><body>
<h1>Method Not Allowed</h1>
<p>The requested method PUT is not allowed for the URL /test.html.</p>
</body></html>
拓展请求方法
除了使用 HTTP/1.1 规范中定义的方法,特定的 HTTP 服务器还能够扩展自定义的方法,被称为扩展方法。例如 PATCH (由 RFC 5789 指定的方法) 用于将局部修改应用到资源。以下是 HTTP 扩展包含的方法。
| 请求方法 | 说明 | HTTP版本 | RFC | Request payload body | Response payload body | 安全的 | 幂等的 | 可缓存的 |
|---|---|---|---|---|---|---|---|---|
| COPY | 在服务器上复制资源 | 1.1 | RFC 4918 | 可选 | 有 | 否 | 是 | 否 |
| LINK | 建立和资源之间的联系 | 1.0 | - | 可选 | 有 | 是 | 是 | 否 |
| LOCK | 允许用户锁定资源 | 1.1 | RFC 4918 | 有 | 有 | 否 | 否 | 否 |
| MKCOL | 允许用户创建集合(如目录) | 1.1 | RFC 4918 | 可选 | 有 | 否 | 是 | 否 |
| MOVE | 允许用户移动/重命名资源 | 1.1 | RFC 4918 | 可选 | 有 | 否 | 是 | 否 |
| PATCH | 部分修改资源 | 1.1 | RFC 5789 | 有 | 有 | 否 | 否 | 否 |
| UNLINK | 断开资源之间的联系 | 1.0 | - | 可选 | 有 | 是 | 是 | 否 |
补充说明。LOCK 允许用户锁定资源。一个常见的场景是,在编辑某个资源的时候将其锁定,以防别人同时对其进行修改。
GET 和 POST 的区别
GET 和 POST 是 HTTP 请求的两种基本方法,要说它们的区别,接触过 WEB 开发的人都能说出一二。譬如,GET 把参数包含在 URL 中,POST 通过 request body 传递参数。巴拉巴拉。甚至你可能自己写过无数个 GET 和 POST 请求,你非常清楚知道什么时候该用什么。当你在面试中被问到这个问题,你的内心充满了自信和喜悦。你轻轻松松的给出了一个标准答案:
1. GET 在浏览器回退时是无害的,而 POST 会再次提交请求;
2. GET 产生的 URL 地址可以被 Bookmark,而 POST 不可以;
3. GET 请求会被浏览器主动cache,而 POST 不会,除非手动设置;
4. GET 请求只能进行 URL 编码,而 POST 支持多种编码方式;
5. GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留;
6. GET 请求在 URL 中传送的参数是有长度限制的,而 POST 没有;
7. 对参数的数据类型,GET 只接受 ASCII 字符,而 POST 没有限制;
8. GET 比 POST 更不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息;
9. GET 参数通过 URL 传递,POST 放在 Request body 中。
很遗憾,这不是我们要的回答!—— 如果我告诉你GET和POST本质上没有区别你信吗?
首先我们来理解清楚请求方法地本质。
GET 和 POST 是什么?HTTP 协议中的两种发送请求的方法。HTTP 是什么?HTTP 是基于 TCP/IP 的关于数据如何在万维网中如何通信的协议。HTTP 的底层是 TCP/IP。所以 GET 和POST 的底层也是 TCP/IP,也就是说,GET/POST 都是 TCP 链接。GET 和 POST 能做的事情是一样一样的。你要给 GET 加上 request body,给 POST 带上 url 参数,技术上是完全行的通的。
那么,标准答案里的那些区别是怎么回事?
在我大万维网世界中,TCP 就像汽车,我们用 TCP 来运输数据,它很可靠,从来不会发生丢件少件的现象。但是如果路上跑的全是看起来一模一样的汽车,那这个世界看起来是一团混乱,送急件的汽车可能被前面满载货物的汽车拦堵在路上,整个交通系统一定会瘫痪。为了避免这种情况发生,交通规则 HTTP 诞生了。HTTP 给汽车运输设定了好几个服务类别,有 GET, POST, PUT, DELETE 等等,HTTP 规定,当执行 GET 请求的时候,要给汽车贴上 GET 的标签(设置 method 为 GET),而且要求把传送的数据放在车顶上(url 中)以方便记录。如果是 POST 请求,就要在车上贴上 POST 的标签,并把货物放在车厢里。当然,你也可以在 GET 的时候往车厢内偷偷藏点货物,但是这是很不光彩;也可以在 POST 的时候在车顶上也放一些数据,让人觉得傻乎乎的。HTTP 只是个行为准则,而 TCP 才是 GET 和 POST 怎么实现的基本。
但是,我们只看到 HTTP 对 GET 和 POST 参数的传送渠道(url 还是 requrest body)提出了要求。标准答案里关于参数大小的限制又是从哪来的呢?
在我大万维网世界中,还有另一个重要的角色:运输公司。不同的浏览器(发起 http 请求)和服务器(接受 http 请求)就是不同的运输公司。 虽然理论上,你可以在车顶上无限的堆货物(url 中无限加参数)。但是运输公司可不傻,装货和卸货也是有很大成本的,他们会限制单次运输量来控制风险,数据量太大对浏览器和服务器都是很大负担。业界不成文的规定是,(大多数)浏览器通常都会限制 url 长度在 2K 个字节,而(大多数)服务器最多处理 64K 大小的 url。超过的部分,恕不处理。如果你用 GET 服务,在request body 偷偷藏了数据,不同服务器的处理方式也是不同的,有些服务器会帮你卸货,读出数据,有些服务器直接忽略,所以,虽然 GET 可以带 request body,也不能保证一定能被接收到哦。
好了,现在你知道,GET 和 POST 本质上就是 TCP 链接,并无差别。但是由于 HTTP 的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。
下课!同学们先别着急,我快速地讲两句(乐)。
其实 GET 和 POST 还有一个重大区别,简单的说:GET产生一个TCP数据包;POST产生两个TCP数据包。
详细的说。对于 GET 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应 200(返回数据)。而对于POST,浏览器先发送 header,服务器响应 100 continue,浏览器再发送 data,服务器响应 200 ok(返回数据)。也就是说,GET 只需要汽车跑一趟就把货送到了,而 POST 得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。因为 POST 需要两步,时间上消耗的要多一点,看起来 GET 比 POST 更有效。因此 Yahoo 团队有推荐用 GET 替换 POST 来优化网站性能。但这是一个坑!跳入需谨慎。为什么?
- GET 与 POST 都有自己的语义,不能随便混用。
- 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的 TCP 在验证数据包完整性上,有非常大的优点。
- 并不是所有浏览器都会在 POST 中发送两次包,Firefox 就只发送一次。
区别总结:
- GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会;
- GET 请求只能进行 URL 编码,而 POST 支持多种编码方式;
- GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留;
- GET 请求在 URL 中传送的参数是有长度限制的(一般为 2k),而 POST 没有;
- 对参数的数据类型,GET 只接受 ASCII 字符,而 POST 没有限制;
- GET 比 POST 更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息;
- GET 参数通过 URL 传递,POST 放在 Request body 中;
- GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。(火狐浏览器除外,它的 POST 请求只发一个 TCP 包)。
POST 请求常见四种参数
- application/x-www-form-urlencoded
- multipart/form-data
- application/json
- text/xml
request里面最重要的是Content-type :如果是application/json,那么request里面就把一个字符串化后的json 数据放在body里,这样服务器可以把它恢复成正确的数据结构,比如说{["ab","bc"]},到服务器端恢复出来就是String XX[],String数组。
Reuqest如果是直接发binary stream过来,服务器这端就用byte[]接收。
对于Response也一样,有字节流类型,有字符串类型(text类型),也有json转成字符串类型(另一种text类型)
HTTP/1.1 协议规定的 HTTP 请求方法有 options、get、head、post、put、delete、trace、connect这几种。其中 POST 一般用来向服务端提交数据,本文主要讨论 POST 提交数据的几种方式。
HTTP 协议是以 ASCII 码传输,建立在 TCP/IP 协议之上的应用层规范。规范把 HTTP 请求分为三个部分:状态行、请求头、消息主体。类似于下面这样:
<method> <request-URL> <version>
<headers>
<entity-body>
协议规定 POST 提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么编码方式。实际上,开发者完全可以自己决定消息主体的格式,只要最后发送的 HTTP 请求满足上面的格式就可以。
但是,数据发送出去,还要服务端解析成功才有意义。一般服务端语言如 php、python 等,以及它们的 framework,都内置了自动解析常见数据格式的功能。服务端通常是根据请求头(headers)中的 Content-Type 字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。所以说到 POST 提交数据方案,包含了 Content-Type 和消息主体编码方式两部分。下面就正式开始介绍它们。
application/x-www-form-urlencoded
这应该是最常见的 POST 提交数据的方式了。浏览器的原生