网络协议
参考
HTTP的特性
- HTTP协议构建于TCP/IP协议之上,是一个应用层协议,默认端口是80
- HTTP是无连接无状态的
HTTP报文
请求报文
HTTP协议是以ASCII码传输,建立在 TCP/IP 协议之上的应用层规范。规范把HTTP请求分为三个部分:状态行、请求头、消息主体。
<method> <request-URL> <version>
<headers><entity-body>
HTTP定义了与服务器交互的不同方法,最基本的方法有4种:GET
、POST
、PUT
、DELETE
-
GET
: 查找服务器中的数据,不会修改和增加数据,不会影响资源的状态-
GET /books/?sex=man&name=Professional HTTP/1.1 Host: www.example.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv: 1.7.6) Gecko/20050225 Firefox/1.0.1 Connection: Keep-Alive
-
-
POST
: 新增资源-
POST / HTTP/1.1 Host: www.example.com User-Agent: Mozilla/5.0 (Windows; U; Window NT 5.1; en-US; rv:1.7.6) Gecko/20050225 Firefox/1.0.1 Content-Type: application/x-www-form-urlencoded Content-Length: 40 Connection: Keep-Alivesex=man&name=Professional
-
【注意】:
-
GET可提交的数据量受到URL长度限制.(HTTP协议内并未对其进行限制,这个限制是特定的浏览器及服务器对它的限制)
-
理论上讲,POST是没有大小限制的,HTTP协议规范也没有进行大小限制,处于安全考虑,服务器软件在实现时会做一定限制
-
GET的数据请求是在URL中、POST的数据请求在HTTP包里
POST提交数据的方式
服务器端通常是根据请求头(headers)中的Content-Type
字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。所以说到POST提交数据方案,包含了Content-Type
和消息主体编码方式两部分。
-
application/x-www-form-urlencoded
- 最常见的POST数据提交方式。浏览器的原生
<form>
表单,如果不设置entype属性,那么最终就会以application/x-www-form-urlencoded
方式提交数据
- 最常见的POST数据提交方式。浏览器的原生
-
multipart/form-data
-
另外一个常见的POST数据提交方式。我们使用表单上传文件时,必须让
<form>
表单的entype等于multipart/form-data
,看下面的栗子: -
POST http://www.example.com HTTP/1.1 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA------WebKitFormBoundaryrGKCBY7qhFd3TrwA Content-Disposition: form-data; name="text"title ------WebKitFormBoundaryrGKCBY7qhFd3TrwA Content-Disposition: form-data; name="file"; filename="chrome.png" Content-Type: image/pngPNG ... content of chrome.png ... ------WebKitFormBoundaryrGKCBY7qhFd3TrwA
-
上面首先生成了boundary用于分割不同的字段,为了避免与正文内容重复,boundary很长很复杂。然后Content-Type
里指明了数据是以multipart/form-data
来编码,本次请求的boundary是什么内容
响应报文
HTTP响应报文与HTTP请求类似,HTTP响应也是由3个部分构成,分别是:
- 状态行
- 响应头(Response Header)
- 响应正文
状态行由协议版本、数字形式的状态码、及相应的状态描述,各元素之间以空格分隔
常见的状态码有以下几种:
200 OK
: 客户端请求成功301 Moved Permanently
: 请求永久重定向302 Moved Temporarily
: 请求临时重定向304 Not Modified
: 文件未修改,可以直接使用缓存的文件400 Bad Request
: 客户端的请求有语法错误,不能被服务器所理解401 Unauthorized
: 请求未授权.这个状态码必须和WWW-Authenticate
报头域一起使用403 Forbidden
: 需要客户端提供验证的信息404 Not Found
: 客户端请求的资源并不存在500 Internal Server Error
: 服务器发生错误,与客户端无关503 Service Unavailable
: 服务器当前不能处理客户端的请求,在一段时间后,服务器可能会恢复正常
[一个HTTP响应的栗子]
HTTP/1.1 200 OKServer:Apache Tomcat/5.0.12
Date:Mon,60ct2003 13:23:42 GMT
Content-Length:112<html>...
条件GET
HTTP条件GET是HTTP协议为了减少不必要的带宽浪费,提出的一种方案
-
HTTP条件GET使用的时机?
客户端之间已经访问过某网站,并打算再次访问该网站
-
HTTP条件GET使用的方法?
客户端向服务器发送一个包询问是否在上一次访问网站的时间后更改了页面,如果服务器没有更新,显然不需要把整个网页传给客户端,客户端只要使用本地缓存即可,如果服务器对照客户端给出的时间已经更新了客户端请求的网页,则发送这个更新了的网页给用户
下面是一个具体的发送接受报文示例:
客户端发送请求:
GET / HTTP/1.1 Host: www.sina.com.cn:80 If-Modified-Since: Thu, 4 Feb 2010 20:39:13 GMT Connection: Close
第一次请求时,服务器端返回请求数据,之后的请求,服务器根据请求中的
If-Modified-Since
字段判断响应文件是否更新:- 否: 服务器返回一个
304 Not Modified
响应,告诉浏览器请求的资源在浏览器上没有更新,可以使用上次的文件
HTTP/1.0 304 Not Modified Date: Thu, 04 Feb 2010 12:38:41 GMT Content-Type: text/html Expires: Thu, 04 Feb 2010 12:39:41 GMT Last-Modified: Thu, 04 Feb 2010 12:29:04 GMT Age: 28 X-Cache: HIT from sy32-21.sina.com.cn Connection: close
- 是: 服务器正常返回
- 否: 服务器返回一个
持久连接
普通模式
: HTTP采用请求/应答
模式,当非Keep-Alive
模式时,每个请求/应答 客户和服务器都要新建一个连接,完成之后立即断开连接持久连接
: 当使用Keep-Alive
模式时,Keep-Alive功能使客户端到服务器的连接持续有效,当出现对服务器的后续请求时,Keep-Alive功能避免了建立或者重写建立连接
在HTTP 1.0版本中,并没有官方的标准来规定Keep-Alive
如何工作,因此实际上它是被附加到HTTP 1.0 协议上,如果客户端浏览器支持Keep-Alive,那么就在HTTP请求头中添加一个Connection: Keep-Alive,当服务器收到附带Connection:Keep-Alive的请求时,它也会在响应头中添加一个同样的字段来使用Keep-Alive.这样一来,客户端和服务器之间的HTTP连接就会被保持,不会断开(超过Keep-Alive规定的时间,意外断电等情况除外),当客户端发送另一个请求时,就使用这条已经建立的连接
在HTTP 1.1 版本中,默认情况下所有连接都被保持,如果假如Connection: close
才关闭.目前大部分浏览器都使用 HTTP 1.1 协议,也就是说默认都会发起Keep-Alive
的连接请求了,所以是否能完成一个完整的Keep-Alive
连接就看服务器设置情况
注意:
- HTTP Keep-Alive 简单说就是保持当前的TCP连接,避免了重新建立连接
- HTTP长连接不可能一直保持,例如
Keep-Alive: timeout=5, max=100
,表示这个TCP通道可以保持5秒,最多接收100次请求就断开 - HTTP是一个无状态协议,这意味着每个请求都是独立的,Keep-Alive没能改变这个结果。另外,Keep-Alive也不能保证客户端和服务器之间的连接一定是活跃的,在HTTP 1.1版本中也如此.唯一能保证的就是当连接被关闭时你能得到一个通知,所以不应该让程序依赖于Keep-Alive的保持连接特性,否则会有意想不到的后果
- 使用长连接,客户端、服务端怎么知道本次传输结束呢?两部分:
- 判断传输数据是否到达了
Content-Length
指示的大小 - 动态生成文件没有
Content-Type
,它是分块传输(chunked),这时候就要根据chunked编码来判断,chunked编码的数据在最后有一个空的chunked块,表明本次传输数据结束
- 判断传输数据是否到达了
Transfer-Encoding
Transfer-Encoding是一个用来标示HTTP报文传输格式的头部值.尽管这个取值理论上可以有很多,但是当前的HTTP规范里实际上只定义了一种传输取值(chunked)
如果一个HTTP消息(请求消息或应答消息)的Transfer-Encoding消息头的值为chunked,那么,消息体由数量未定的块组成,并以最后一个大小为0的块为结束
每一个非空的块都以该块包含数据的字节数(字节数以十六进制表示)开始,跟随一个回车换行(CRLF),然后是数据本身,最后块CRLF结束.在一些实现中,块大小和CRLF之间填充有白空格(0x20)
最后一块是单行,由块大小(0),一些可选的填充白空格,以及CRLF。最后一块不再包含任何数据,但是可以发送可选的尾部,包含消息头部。消息最后以CRLF结尾
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked25
This is the data in the first chunk1A
and this is the second one0
注意:
- chunked和multipart两个名词在意义上有类似的地方,不过在HTTP协议当中,这两个概念则不是一个类别的。multipart是一种Content-Type,标示HTTP报文内容的类型,而chuncked是一种传输格式,标示报头将以何种方式进行传输
- chuncked传输不能事先知道内容的长度,只能靠最后的空chunk块来判断,因此对于下载请求来说,是没有办法事先进度的。在浏览器和下载工具中,偶尔我们也会看到有些文件是看不到下载进度的,即采用chunked方式进行下载
- chunked的优势在于,服务器端可以边生成内容边发送,无需事先生成全部的内容。HTTP/2不支持 Transfer-Encoding: chuncked, 因为HTTP/2 有自己的 streaming传输方式
HTTP Pipelining(HTTP 管线化)
默认情况下HTTP协议中每个传输层连接只能承载一个HTTP请求和响应,浏览器会收到上一个请求的响应之后,再发送下一个请求。在使用持久连接的情况下,某个连接上消息的传递类似于:请求1 -> 响应1 -> 请求2 -> 响应2 -> 请求3 -> 响应3
HTTP Pipelining(管线化)是将多个HTTP请求整批提交的技术,在传输过程中不需要等待服务器的回应。使用HTTP Pipelining技术之后,某个连接上的消息变成了类似这样请求1 -> 请求2 -> 请求3 -> 响应1 -> 响应2 -> 响应3
注意下面几点:
- 管线化机制通过持久连接(persistent connection)完成,仅 HTTP/1.1 支持此技术(HTTP/1.0 不支持)
- 只有GET和HEAD请求可以进行管线化,而POST则有所限制
- 初次创建连接时不应启动管线机制,因为对方(服务器)不一定支持HTTP/1.1版本的协议
- 管线化不会影响响应到来的顺序
- HTTP/1.1要求服务器端支持管线化,但并不要求服务器也对响应进行管线化处理,只是要求对于管线化的请求不失败即可
- 由于上面提到的服务器端问题,开启管线化很可能并不会带来大幅度的性能提升,而且很多服务器端和代理程序对管线化的支持并不好,因此现代浏览器和Chrome和Firefox默认并未开启管线化支持
会话跟踪
-
什么是会话
客户端打开与服务器的连接发出请求到服务器响应客户端请求的全过程称之为会话
-
什么是会话跟踪
是对同一个用户服务器的连续的请求和接受响应的监视
-
为什么需要会话跟踪
浏览器与服务器之间的通信是通过HTTP协议进行的,而HTTP协议是"无状态"协议,它不能保存客户的信息,即一次响应完成之后连接就断开了,下一次的请求需要重新连接,这样就需要判断是否是同一个用户,所以才会有会话跟踪技术来实现这种要求
【会话跟踪常用的方法】
- URL重写:
- URL是Web上特定页面的地址,URL重写的技术就是在URL结尾添加一个附加数据以标识该会话,把会话ID通过URL的信息传递过去,以便在服务器端进行识别不同的用户
- 隐藏表单域
- 将会话ID添加到HTML表单元素中提交到服务器,此表单元素并不在客户端显示
- Cookie
- Cookie是Web服务器发送给客户端的一小段信息,客户端请求时可以读取该信息发送到服务器端,进而进行用户的识别。对于客户端的每个请求,服务器都会将Cookie发送到客户端,在客户端可以进行保存,以便下次使用.
- 客户端可以采用两种方式来保存这个Cookie对象,一种方式是保存在客户端内存中,称为临时Cookie,浏览器关闭后这个Cookie对象将消失。另外一种方式是保存在客户机的磁盘上,称为永久Cookie。以后客户端只要访问该网站,就会将这个Cookie再次发送到服务器上,前提是这个Cookie在有效期内,这样就实现了对客户的跟踪
- Cookie是可以被客户端禁用的
- Session
- 每个客户都有一个不同的session,各个用户之间是不能共享的,是每个用户所独享的,在session中可以存放信息
- 在服务器端会创建一个session对象,产生一个sessionID来标识这个session对象,然后将这个sessionID放入到Cookie中发送到客户端,下一次访问时,sessionID会发送到服务器,在服务器端进行识别不同的用户
- Session的实现依赖于Cookie,如果Cookie被禁用,那么session也将失效
跨站攻击
参考
- CSRF(Cross-site request forgery, 跨站请求伪造)
CSRF顾名思义,是伪造请求,冒充用户在站内的正常操作。我们知道,绝大多数网站是通过cookie等方式辨别用户身份,再予以授权。所以要伪造用户的正常操作,最后的办法是通过XSS或链接欺骗等途径,让用户在本机(即拥有身份cookie的浏览器端)发起用户所不知道的请求
栗子:一论坛的发帖是通过GET请求访问,点击发帖之后JS把发帖内容拼接成目标URL并访问http://example.com/bbs/create_post.php?title=标题&&content=内容
那么,我只需要在论坛中发一贴,包含一链接http://example.com/bbs/create_post.php?title="我是脑残"&&content="哈哈"
只要用户点击了这个链接,那么他们的账户就会在不知情的情况下发布这一帖子。
**如何防范CSRF攻击?**可以注意以下几点:
- 关键操作(如发帖等)只接受POST请求
- 验证码:
- CSRF攻击的过程,往往是在用户不知情的情况下构造网络请求。所以如果使用验证码,那么每次操作都需要用户进行互动,从而简单有效的防御了CSRF攻击
- 但是如果你在一个网站中,做出任何举动都要输入验证码会严重影响用户体验,所以验证码一般只出现在特殊操作里面,或者在注册时候使用
- 检查Refer
- 常见的互联网页面与页面之间是存在联系的,比如你在
www.baidu.com
应该找不到通往www.google.com
的链接的,再比如你再论坛留言,那么不管你留言后重定向到哪里去,之前的那个网址一定会包含留言的输入框,这个之前的网址就会保留在新页面头文件的Referer中 - 通过检查Referer的值,我们就可以判断这个请求是合法的还是非法的,但是问题出在服务器不是任何时候都能接受到
Referer
的值,所以Referer Check
一般用于监控CSRF攻击的放生,而不用来抵用攻击
- 常见的互联网页面与页面之间是存在联系的,比如你在
- Token
- 目前主流的做法是使用Token抵御CSRF攻击:
- CCSRF攻击主要成功的条件在于攻击者能够预测所有的参数,从而构造出合法的请求.所以根据不可预测性原则,我们可以对参数进行加密从而防止CSRF攻击
- 另一个更通用的做法是保持原有参数不变,另外添加一个参数Token,其值是随机的。这样攻击者因为不知道Token而无法构造出合法的请求进行攻击
- Token使用原则:
- Token要足够随机 (这样才无法预测)
- Token是一次性的,即每次请求成功之后要更新Token(增加攻击难度,预测难度)
- Token要注意保密性(敏感操作使用POST,防止Token出现在URL中)
- 目前主流的做法是使用Token抵御CSRF攻击:
HTTPS
参考
HTTP请求都是明文(没有经过加密的信息)传输,如果HTTP请求被黑客拦截,并且里面含有银行卡密码等敏感数据的话,会非常危险.为了解决这个问题,Netscape公司指定了HTTPS协议,HTTPS可以将数据加密传输,也就是传输的是密文,即便黑客在传输过程中拦截到数据也无法破译,这就保证了网络通信的安全.
密码学基础
明文: 未被加密过的原始数据
密文: 明文被某种加密算法加密之后,会变成密文,从而确保原始数据的安全。密码也可以被解密得到原始的明文
密钥: 密钥是一种参数,它是在明文转换为密文或密文转换为明文的算法中输入的参数。密钥分为对称密钥与非对称密钥,分别应用在对称加密和非对称加密上
对称加密: 对称加密又叫做私钥加密,即信息的发送方和接收方使用同一个密钥去加密和解密数据。对称加密的特点是算法公开、加密和解密速度快,适合于对大数据量进行加密,常见的对称加密算法有: DES、3DES、TDEA、Blowfish、RC5和IDEA
其加密过程如下: 明文 + 加密算法 + 私钥 => 密文
解密过程如下: 密文 + 解密算法 + 私钥 => 明文
私钥: 对称加密中用到的密钥叫做私钥,私钥表示个人私有的密钥,即该密钥不能被泄漏
由于对称加密的算法是公开的,所以一旦私钥被泄漏,那么密文就很容易被破解,所以对称加密的确定是密钥安全管理困难
非对称加密: 使用一对密钥公钥
和私钥
.私钥被自己保存,不能对外泄漏.公钥指的是公开的密钥,任何人都可以获得该密钥。
被公钥加密过的密文只能被私钥解密,过程如下:
明文 + 加密算法 + 公钥 => 密文
、密文 + 解密算法 + 私钥 => 明文
被私钥加密故的密文只能被公钥解密,过程如下:
明文 + 加密算法 + 私钥 => 密文
、 密文 + 解密算法 + 公钥 => 明文
HTTPS通信过程
HTTPS = HTTP + SSL/TLS
,过程如下: 客户端向服务器发送一条 HTTP 报文.先使用HTTP协议生成报文,然后通过SSL/TLS 对报文进行加密. 服务端得到报文后,先经过SSL/TLS对报文进行解密,然后再使用HTTP协议解析明文.具体步骤如下:
-
客户端向服务器发起HTTPS请求,连接到服务器的443端口
-
服务器端有一个密钥对,即公钥和私钥,公钥公开出去.私钥保存再服务器内部.
-
服务器将自己的公钥发送给客户端
-
客户端收到服务端的公钥后,会对公钥进行检查,验证其合法性,如果发现公钥有问题,那么HTTPS传输就无法继续。如果公钥合格,那么客户端会生成一个随机值,这个随机值就是用于进行对称加密的密钥(称之为 client key)
-
客户端发起HTTPS中的第二次请求,使用服务器端的server public key对client key进行加密之后,将密文发送给服务端
-
服务端收到client key后,使用sever private key进行解密.得到client key的明文
-
之后客户端和服务端都使用 client key 进行加解密.
根据上述所述: HTTPS的流程是先使用非对称加密交换 对称加密的密钥. 之后使用对称加密进行传输