一、http报文结构和方法
http请求报文格式:
http应答报文格式:
以下为http请求、响应报文结构:
URL的格式:
http1.1中请求的方法有:
GET:请求资源,如果请求的资源时文本,服务端就直接返回对应的文本资源,如果是像CGI(Common Gateway Interface 通用网关接口)那样的程序,则返回执行后的输出结果。
POST:向服务器传输实体的主体(虽然GET也可实现,但一般是使用POST)。
HEAD:获得报文首部,类似GET但不返回报文主体部分,可用于获得URI的有效性及资源更新的日期时间等。
OPTIONS:获得对请求URI指定的资源支持的方法,如GET、POST、HEAD等。
PUT:向服务器传输文件,类似FTP上传文件一样,一般网站不开放该方法,除非配合WEB应用验证机制或架构设计采用REST标准的网站。
DELETE:删除文件。
TRACE:追踪路径,客户端使用该方法发送请求时在Max-Forwards首部字段中填入数值,每经过一个服务器该值会减1,若该值变为0则当前服务器返回状态码200响应。
CONNECT:请求用隧道协议连接代理,主要使用SSL和TLS将通信内容加密后经网络隧道传输,使用connect方法的格式为:CONNECT 代理服务器名:端口号 HTTP版本,如:
POST与GET的区别:POST的主要目的并不是像GET那样获取响应的主体内容,如下图所示,POST主要是向服务端发送实体主体的数据,而GET请求一般不会使用实体主体。也就是说POST主要用来向服务端推数据,GET用来从服务端拉数据。
由于post和get的使用差异才导致了二者的以下区别:
1)、get发送的数据有长度和类型限制,post则没有。因为发送数据时,POST是向实体主体添加数据,GET方法是向URL添加数据,URL是字符类型,而且其长度是受限制的(最大2048个字符)。
2)、因为POST发送的数据是在URL当中的,所以GET的安全性比POST要差,在发送密码等敏感信息时不要使用GET。
3)、对于浏览器来说,它会因为POST的特性而对其在浏览器上的表现作出额外的限制:POST请求不会被缓存(除非手动设置);POST请求不会保留在浏览器历史记录中;POST请求不能被收藏为书签;
4)、还是对于浏览器来说,GET请求在三次握手之后直接发送get请求头和数据(服务器返回200 OK响应),POST请求会在三次握手之后先发送post请求头,在服务器返回100 Continue响应后浏览器再发送数据,这时候服务器返回200 OK响应。因为POST会分两次传输,所以一般认为POST比GET要慢。我在网上看到有人说并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。
二、cookie、session、token
1、HTTP的无状态
在HTTP1.1之前,每次请求-应答数据都要建立-关闭一个连接,服务器认为每次请求都是一个全新的请求,无论该请求是否来自同一地址,因此,HTTP通信是无状态的,当前请求并不会记录它的上一次请求信息。在HTTP1.1中默认允许TCP连接保持,以节省建立连接所耗费的时间,但HTTP协议依然保持无状态的特性。因为http是无状态的,那么如何使用http将上下文请求(多次请求)进行关联呢?答案就是使用cookie。cookie分为会话(内存)cookie和持久(硬盘)cookie,比如网站自动登录功能就是利用的持久cookie,而一些网站在关闭后(不关闭浏览器)再次打开依然是登陆状态,这就是利用会话cookie的效果。
2、cookie
使用cookie登录相当于是有状态的http,每次请求(甚至关闭浏览器后的再次请求)服务器都可以通过cookie来确认用户是已登录的,相当于服务器在维护着一个seesion。一般的流程是客户首次登录输入用户名密码,服务器登录验证后生成一个sessionID并保存,然后将这个sessionID通过cookie返回给客户端,客户端每次请求都携带保存sessionID的cookie,服务器拿着客户端发来的cookie与保存的seesionID进行验证用户的权限。
当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含session id,则为客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发送给服务器。cookie数据存放在客户的浏览器上,session是相对于服务器而言,session数据放在服务器上。
3、token
token 的认证方式是无状态的,服务端不保存登陆状态,也就是服务器不保存sessionID来维护与客户端的session。使用token的流程是客户首次登录输入用户名密码,服务器登录验证后签发一个token给客户端(比如说用HMAC-SHA256算法,加上一个他人不知道的密钥,对数据做一个签名,把这个签名和数据一起作为token ,由于密钥别人不知道,所以无法伪造token)。客户端也会将token保存起来,以后发送的每一个请求都携带token,服务器接收请求后拿到 token 去解析认证(比如用同样的HMAC-SHA256 算法和同样的密钥,对数据再计算一次签名, 和客户发来的token 中的签名做个比较,如果不相同就是认证失败)。
使用token的优势:
服务器不必保存token来查询,而是直接对客户端发来的token进行验证。
可以携带其他信息,比如携带具体权限信息之类的,避免了再去查库。
如果使用了多台服务器的负载均衡,两条请求发给了不同的服务器的话使用cookie就不能达到效果。
请求中发送token而不再是发送cookie能够防止CSRF(跨站请求伪造)。即使在客户端使用cookie存储token,cookie也仅仅是一个存储机制而不是用于认证。
请求需要跨域的接口的时候 cookie 就力不从心了,不同域就不会携带 cookie。
cookie主要是与浏览器交互,对于移动端不太好支持,移动端使用token 就方便得多。
三、http首部和响应状态码
1、http1.1首部字段
http首部字段由字段名+字段值构成,中间用分号“:”分隔,如果包含多个字段值的话用逗号“,”分隔,如:“Content-Type: text/html”,"Keep-Alive: timeout=15, max=100"。
通用首部字段:
请求首部字段:
响应首部字段:
实体首部字段:
如果按照缓存代理和非缓存代理的行为,可以将http首部字段分为端到端(End-to-end)首部和逐跳(Hop-by-hop)首部,逐跳首部只对单次转发有效,会因通过缓存或代理而不再转发。自http/1.1起,如果要使用hop-by-hop首部,需提供Connection首部字段,http/1.1中的逐跳首部字段有以下8个,除这8个首部字段外其它首部字段都属于端到端首部:
关于http首部字段的详细说明参考<<图解http>>。
2、http响应状态码
1xx :接收的请求正在处理。
200 OK:请求正常处理。
204 No Content:请求处理成功,且相应无资源返回。
206 Partial Content:请求处理成功,且该请求是范围请求。
301 Moved Permanently:永久性重定向即页面永久性移走,浏览器收到后会自动跳转到一个新的URL地址,且应该将保存的旧地址替换为新地址。
302 Found:页面临时性重定向,浏览器收到后会自动跳转到一个新的URL地址。
303 See Other:类似302功能,但它明确表示客户端应该使用GET方法重定向到指定资源。301、302标准是禁止将POST方法改变成GET方法的,但实际上几乎所有的浏览器都对301、302响应做出把POST改成GET后再次发送请求的做法。
304 Not Modified:资源已找到,但附带条件未满足。附带条件指采用GET方法的请求报文中If-Match、If-None-Match、If-Modified-Since等。
307 Temporary Redirect:类似302功能,但它明确表示客户端不应使用GET方法重定向到指定资源。
400 Bad Request:请求报文中存在语法错误,浏览器会像200 OK一样对待该状态码。
401 Unauthorized:发送的请求需要有http认证(BASIC认证、DIGEST认证)的信息,若之前已进行过一次请求,则表示用户认证失败。浏览器初次接收到401响应会弹出认证用的对话框。
403 Forbidden:访问被拒绝。
404 Not Found:资源未找到。
500 Internal Server Error:服务器执行出错。
502 Bad Gateway 网关错误。
503 Service Unavailable:服务器超负载或停机维护。如果知道服务器恢复的时间最后写入RetryAfter首部字段再返回给客户端。
504 Gateway Timeout网关超时
四、HTTPS
现代加密方法中一般加密算法是公开的,但解密用的秘钥是保密的,比如发送方使用加密算法和秘钥对数据进行加密,接收方使用秘钥和解密算法对数据进行解密,这种发送方和接收方使用同一个秘钥的方式称为对称秘钥加密(共享秘钥加密)。对称加密的关键是怎样保证将秘钥安全的发送给对方,这可以通过非对称加密来实现。
非对称秘钥加密(公开秘钥加密)使用两把秘钥,一把私有秘钥只有自己知道,一把公开秘钥用来发送给对方:甲方生成私钥和公钥后向乙方发送公钥,乙方使用公钥对数据进行加密后再将加密数据发送给甲方,甲方再根据私钥对数据进行解密,发送的公钥和加密数据即使被第三方截取到也不能对数据进行解密,因为解密需要私钥。
对于HTTPS来说,在数据通信开始前会先进行SSL握手,SSL握手即为通过非对称加密方式将秘钥从客户端发送给服务端,然后使用对称加密来进行正常的HTTP数据通信:
SSL握手:客户端向服务端发起请求,服务端将公钥和证书发送给客户端,客户端通过证书确认公钥有效后会生成一个随机数串,并根据这个随机数串来生成秘钥,然后通过公钥对秘钥进行加密后发送给服务端,服务端使用私钥对数据进行解密后就得到了秘钥,这样以后就可以通过这个秘钥来进行正常的通信了(通信的数据使用对称加密方法来加密)。
在上面的SSL握手中如何确保公钥就是来自确信的服务端呢,比如在公钥传输途中被攻击者替换掉了,可以使用数字证书认证机构(CA,Certificate Authority)颁发的证书来解决该问题。首先服务端的运维人员向CA申请公钥的证书,CA验证后会发放公钥的数字签名证书,证书里面包含申请者的信息,数字签名后的公钥,有效时间和签名,客户端在连接的时候会对服务端发来的证书做验证,浏览器一般都会内置CA的公开秘钥,可以使用CA的公开秘钥对证书上的数字签名进行验证,验证通过表示来自服务端的公钥是值得信赖的。
CA证书的另一个作用是可确认对方服务器背后的运营企业是否真实存在,一般这种网站会以绿色背景显示其域名,或有一个小锁的图标,点击它会提示当前连接是安全的,还可以查看证书的组织名和颁发机构等信息。
可以说HTTPS = TLS/SSL + HTTP,如下所示,它通过加密解决了攻击者窃听和篡改请求或响应的数据,通过使用证书解决了无法验证通信方身份的问题。
HTTPS相对HTTP多出了SSL通信以及浏览器服务端的加密解密运算,所以其可能会比HTTP要慢2到100倍,可以使用SSL加速器这种(专用服务器)硬件来改善该问题。HTTPS通信中的SSL握手通信具体步骤可以在《图解HTTP》书中查看。
HTTPS默认端口号为443,HTTP协议代理服务器常用端口号:80/ 8080/ 3128/ 8081/ 9098。
五、用户身份认证
用户身份认证也称为http认证,主要有BASIC认证(基本认证)、DIGEST认证(摘要认证)、SSL客户端认证、FormBase认证(基于表单认证),此外还有Windows统一认证,包括Keberos认证、NTLM认证。
1、BASIC认证流程:客户端请求资源时服务器会返回带401响应状态码和WWW-Authenticate首部字段的响应,该字段内包含认证方式(BASIC)和认证信息。客户端收到401后在Authorization字段上添加用户ID和密码(一般浏览器会提示用户输入用户名和密码),二者应以冒号连接,并以Base64编码后发送。认证成功后服务器返回状态码200,失败会返回401。
BASIC认证的缺点是密码明文传输(只是经过了Base64编码)。Base64就是一种基于64个可打印字符来表示二进制数据的方法,具体转换步骤:
第一步,将待转换的字符串每三个字节分为一组,每个字节占8bit,那么共有24个二进制位。
第二步,将上面的24个二进制位每6个一组,共分为4组。
第三步,在每组前面添加两个0,每组由6个变为8个二进制位,总共32个二进制位,即四个字节。
第四步,根据Base64编码对照表(0对应'A',1对应'B',...63对应'/')获得对应的值。
2、DIGEST认证同BASIC认证一样采用“质询/响应”的方式,但增加了密码加密。其认证流程是:客户端请求资源时服务器会返回带401响应状态码和WWW-Authenticate首部字段的响应,该字段内包含认证方式(DIGEST)和认证信息,且该字段内必须包含realm和nonce这两个字段,客户端会向服务器回送这两个字段值,其中realm通常设为服务器的域名,nonce是一个随机数。客户端收到401后在Authorization字段上添加username、realm、nonce、uri、response(根据用户名、密码等字段值组合后进行MD5运算的值)字段值后发送(一般浏览器会提示用户输入用户名和密码)。服务器收到客户端响应后确认认证的正确性(通过客户端的用户名获得保存的密码,同样与其它字段组合后获得MD5值进行对比),通过的话就返回客户端请求的资源。
DIGEST认证存在的问题是现在服务器对于用户密码一般都不是明文保存,而是采用MD5值或MD5 + SALT的方式,这样客户端和服务器的密码就不能统一。所谓的MD5 + SALT就是指服务器不是直接计算密码的MD5值,而是再加上另外一个SALT值来计算出MD5值来保存,这个SALT值一般是服务器随机生成的一个长度够长的值,所以只有服务器知道,这样更加安全,避免黑客获得用户密码的散列值后通过查散列值字典(例如MD5密码破解网站),得到某用户的密码。
DIGEST认证的另一个问题是虽然它能提供防止密码被窃听的保护机制,但不存在防止用户伪装的的保护机制。
3、SSL客户端认证是指将客户端证书发给客户,并且客户端必须安装此证书,当客户端请求服务器时服务器会发送Certificate Request报文,要求客户端提供客户端证书,用户选择客户端证书后将证书信息以Client Certificate报文方式发送给服务器,服务器验证通过后方可领取证书内客户端的公开秘钥,然后开始HTTPS通信。
SSL客户端认证可以通过客户端证书来确认当前就是实际用户,但缺点是需要从认证机构购买客户端证书,服务器运营者也可以自己搭建认证机构,但要维持安全运行就会产生相应的费用,一般银行等机构会使用该认证机制。
4、基于表单认证:基于表单的认证并不是HTTP协议中的标准,一般的流程是客户端将ID和密码等登陆信息以HTTPS通信来发送给服务器,服务器验证客户端的登陆信息后会发放用以识别用户的Session ID,且将用户的认证状态与Session ID绑定后记录在服务端,向客户端返回响应时会在首部字段Set-Cookie内写入Session ID(Session ID应该使用难以揣测的字符串,且服务器也需要进行有效期管理,保证其安全性,另外,为减轻跨站脚本攻击(XSS)造成的损失,建议事先在Cookie内加上httponly属性),客户端收到Session ID后会将其作为Cookie保存在本地,下次向服务器发送请求时,浏览器会自动发送Cookie,所以Session ID也随之发送,这样服务器可以通过接收到的Session ID识别用户和其认证状态。
现在的web网站多为使用基于表单的认证。