详谈HTTP和HTTPS的加密
一、前言
HTTP协议(超文本传输协议)是客户端与服务器通过TCP协议建立连接的基础上,由客户端发出请求(request),服务器做出相应(response)的方式实现双方高效通信的一种应用层协议。所有的WWW文件都需要遵守这个标准,我们所熟悉的HTML语言就是通过HTTP来进行传输的。
我们想要在网络中传输数据,肯定不可能是以对象的方式去传输,TCP协议是面向字节流的传输协议,所以我们会通过序列化将我们要传输的数据转化为字节流,传输到对方手上后,对方再进行反序列化,将字节组装成原来的数据。这些内容在TCP部分再详细介绍。
总结一下:HTTP是一个基于请求与响应,无状态的应用层协议
二、认识URL
url(uniform resource locator)统一资源定位符,是因特网上的WWW服务程序用于指定位置信息的表示方法,我们平时口语的网址,其实就是在说URL。
http://user:pass@www.xxx.jp:80/dir/index/htm?uid=1#ch1
http://
表示该URL的协议方案名称;
user:pass
表示登录信息,用于认证;
www.xxx.jp
表示服务器的地址
80
表示服务器的端口号
dir/index.htm
是以层级形式表示的文件路径
uid=1
表示查询字符串,即携带的请求所携带的参数
ch1
表示的是片段标识符,比如连续的几个页面,以1,2,3···进行编号
/、?、:等字符在url中会有特殊含义,所以不可以在一个url中随便出现,如果参数中带有这些特殊符号,我们就需要对这些特殊字符先进行转义。转义规则为:将需要转义编码的字符转为16进制,从右往左取四位(不足4位直接处理),每2位作为一位,在前面加上%,编码成为%XY的格式。我们可以通过urlencode的工具帮助我们实现该功能
输出结果如下:
也可以通过urldecode再将转移过的字符变回原来的样子,这里就不再演示了
三、HTTP协议的格式
HTTP请求
HTTP的请求由四部分构成,分别是:请求行、请求报头、空行、请求正文
请求行作为请求部分的首行,其格式为[方法] + [URL] + [版本]
。它会告诉我们当前请求的使用的方法、要访问的主机URL以及当前所使用的HTTP版本信息。
接下来的部分则是请求报头,其中的内容均以键值对的形式出现,并且每个键值对占据一行,其中非常重要的一个参数是Content-Length
,它的value是一个整数,代表的是下面请求正文的长度。
在请求报头后面会跟上一个空行,什么内容都没有,其作用就是帮助分割请求报头和请求正文。所以空行之后紧接着的就是请求正文,请求正文的结尾我们如何知道呢?如果在通信过程中,客户端不断发送请求,那么很有可能一个请求后面接着的就是另一个请求,如果没有找到该请求正文的末尾,就会跟下一个请求之间产生混淆,这就是为什么在请求报头中需要携带Content-Length
的原因了,通过请求正文的开始和请求正文的长度,我们可以计算出请求正文的结尾。
HTTP响应
响应构成与请求类似,也有响应行、响应报头、空行、响应正文组成。
其中第一行是响应行,格式是[版本号] + [状态码] +[状态码解释]
。它所携带的信息是当前HTTP的版本,以及对请求的状态码,和对该状态码的描述信息,如常见的404 NOT FOUND。
响应报头中也以键值对形式保存信息,并且每个键值对单独占据一行,其中也有一个属性Content-Length
表示响应正文部分的长度,如果服务器返回了一个html页面,则html页面的所有内容都会包含在响应正文当中。
空行是用来分割响应报头和响应正文的。
四、HTTP的方法
方法 | 说明 | 支持的HTTP协议版本 |
---|---|---|
GET | 获取资源 | 1.0、1.1 |
POST | 传输实体主体 | 1.0、1.1 |
PUT | 传输文件 | 1.0、1.1 |
HEAD | 获得报文首部 | 1.0、1.1 |
DELETE | 删除文件 | 1.0、1.1 |
OPTIONS | 查询支持的方法 | 1.1 |
TRACE | 追踪路径 | 1.1 |
CONNECT | 要求使用隧道协议连接代理 | 1.1 |
LINK | 建立和资源之间的联系 | 1.0 |
UNLINK | 断开连接关系 | 1.0 |
其中最常用的是GET和POST方法,别的方法建议大家了解,至少知道除了GET和POST方法外,还有PUT和HEAD其他方法。
五、HTTP的状态码
类别 | 原因解释 | |
---|---|---|
1XX | Informational(信息性状态码) | 接受的请求目前正在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行进一步操作才能完成请求 |
4XX | Client Error(客户端错误状态码) | 客户端错误,请求中包含语法错误或者无法完成客户端的请求 |
5XX | Server Error(服务器错误状态码) | 服务器处理请求出现错误 |
常见的状态码如200(表示请求处理成功)一般用于GET和POST,404(资源找不到)、403(禁止访问)、302(需要重定向)、504(Bad Gateway)等。
其中需要特殊说明的是301(Moved Permanently)永久移动,请求的资源已经永久被移动到新的URI,返回信息中会包括这个新的URI,浏览器收到这个信息会自动帮用户定向到新的URI去,并且今后所有新的请求都应该使用新的URI去代替。而302则是(Found),即临时移动,资源只是临时被移动到新的URI,后续客户端应该继续使用原有的URI进行访问。
403(Forbidden)禁止访问,服务器理解客户端的请求,但是拒绝执行此请求,比如请求访问的文件权限并未开放;404(Not Found)服务器无法根据客户端的请求找到对应的资源,**注意:4XX的代码都是客户端错误码,403是客户端想要访问不能访问的资源,而404则是无法根据客户端的请求找到对应的资源,服务器运行是正常的!千万不要以为是服务器出现了错误才导致这些结果!**我们所看到的404Not Found页面也都是网页设计人员自己制作出来的,当服务器找不到客户端请求的资源时,就会跳转到这个404页面。
六、HTTPS
HTTP默认的端口号是:80而HTTPS则是:443,这只是HTTP与HTTPS的一个区别,我们下面从更加深层次的方面了解两个协议的不同。
HTTP协议是以明文的方式发送内容,不会提供任何方式的数据加密,如果在客户端与服务器中间有一个“黑客”,他可以轻而易举地截取客户端与服务器之间的传输报文,就可以轻松地读取其中的一些信息,因此HTTP协议的页面在谷歌浏览器中会提示用户“不安全”。而HTTPS(Hypertext Transfer Protocol Secure)超文本传输安全协议,则会对以HTTP协议传输的数据进行加密,从而达到数据传输的安全可靠。
HTTPS是基于HTTP传输协议的,它是在HTTP的基础上建立SSL加密层,对传输的数据实现加密,我们可以理解为HTTPS是打了疫苗的HTTP。
通过上面的图示我们可以知道HTTPS和HTTP同属应用层协议,但是HTTPS利用SSL/TLS来对数据进行加密,SSL(Secure Socket Layer安全套接字协议)和TLS(Transport Layer Security安全传输层协议)是位于应用层与传输层之间的协议。
七、HTTPS的加密过程
我们都知道HTTP和HTTPS是建立在TCP协议之上的,所以我们假设后续的例子中,所有的客户端与服务器已经实现了三次握手建立连接,并在完成传输后实现四次挥手。
需要加密,我们就要引出一个名词:秘钥。秘钥是用来进行加密的重要工具。同时我们再引出一个加密方式:对称加密,举一个简单的小例子:
这种客户端与服务器都通过相同的秘钥实现一方加密,另一方解密的操作就是对称加密,确实对称加密实现了数据加密,我们在网络上传输的并不是真正的数据0110而是加密后的不知名数据。但是这种方式有一个严重的问题,那就是我们用来进行加密的秘钥1234它本身也是数据,我们该怎么把它告知给服务器让服务器用它来进行解密呢?直接发送也会有被黑客盗取的风险,如果再给秘钥也加上秘钥,秘钥的秘钥是否也需要秘钥来加密?这就会变成一个无穷递归问题。
所以仅仅使用对称加密是不行的,HTTPS由衍生出了非对称加密:公钥和私钥,我们在使用GitHub时肯定见到过这两个名词吧。通常情况下:公钥用来进行加密,私钥用来进行解密,当使用非对称和对称加密结合的方式,就可以实现HTTPS的密钥协商过程了,具体如下图所示:
当客户端向服务器发起秘钥协商的请求时,服务器会先将自己的公钥发送给客户端,客户端会先使用服务器的公钥,对自己对称加密秘钥进行加密操作,然后再将加密过的秘钥发送给服务器,服务器收到后,就会使用自己的私钥对其进行解密,注意私钥仅仅存放在服务器内部一个安全的地方,并不会在网络上流通,外人是不可能知道这个私钥的!在私钥解密后,服务器就安全地拿到了客户端的秘钥,双方就可以使用该秘钥进行我们上面所谈到的对称加密操作了。
有的同学肯定会问既然非对称加密的方式这么这么安全,我们直接使用非对称加密方式让双方进行通信就行了,为什么还是要在后面转换为对称加密的方式呢?原因就是非对称加密的算法为了安全性,其逻辑一般都设计的较为复杂,同时经过上面的例子我们也能看出,非对称加密的步骤繁琐,因此效率也更低。对称加密速度更快,网络通信时,速度的快慢是用户体验感的决定性因素。
但是这也并不是绝对安全的,仍然存在着安全漏洞,如果有一个中间服务器,替换了客户端收到的服务器公钥,替换成了自己的公钥,并利用自己的私钥通过上面我们谈到的对称加密方式而解开了客户端的对称秘钥,并使用客户端的对称秘钥代替它去访问服务器,也会造成数据泄露,图示如下:
再将整个过程详细捋一遍,首先客户端之前在服务器访问时获取到的服务器公钥已经存放在本地了,黑客将其替换为了自己的公钥,这样客户端的请求就发给了中间服务器,经过我们之前说过的,用公钥加密将自己的对称秘钥发送给了中间服务器,中间服务器再拿着自己的私钥解密,这样就拿到了客户端的对称秘钥,与客户端建立了连接,客户端就使用对称加密的方式向中间服务器发送自己的数据!(客户端并不知道自己访问的是中间服务器,还以为自己在与真正的服务器通信),经过①之后,中间服务器获取到了客户端想要与服务器通信的数据。
而中间服务器也可以作为一个客户端,它向真正的服务器发起请求,服务器见到请求来了,就将自己的公钥交给它,它再以公钥加密自己的对称秘钥,经过非对称加密方式,与服务器建立连接,并将从客户端那里拿到的数据,发送给服务器,代替客户端与服务器通信!这就是②的过程。
服务器也并不知道中间有个人插了进来,还以为自己正在与客户端通信,就将需要发送给客户端的数据全部发送给了中间服务器,如③所示。
中间服务器拿到了服务器的响应数据,就可以窃取其中的信息,甚至再将该信息加工包装变成错误的信息发送给客户端,客户端还以为自己收到的是服务器发来的正确数据,如④所示。
打个比方就好像你和朋友正在进行对话,突然神不知鬼不觉地出现了一个人,将你的话转达给你的朋友,再将你的朋友的话转达给你,他可以随意修改你们想要表达的本意,但你们双方却并不知情,还以为自己是在与真正的对方直接交流。所以想要在HTTPS中安全通信,必须要解决:远端服务器的身份如何认证的问题;以及怎么防止信息中途被篡改的问题。
先说一下如何解决中间信息被篡改的问题,实际上在双方通信时,会将通过HTTPS传输的文本信息经过哈希算法处理后生成一个定长的字符序列,我们称之为文本摘要,这个哈希算法要求原文被修改任何一个字符都会导致生成的文本摘要发生变化,因此在发送方发送的文本信息后面还会附加上这个文本摘要,接收方在接收到文本信息后,会用同样的算法再生成一个文本摘要,并将新生成的文本摘要与之前的发送方附加的文本摘要进行对比,如果相同,证明信息在传输过程中没有被篡改,如果不同则证明中间信息被篡改了。同时为了保护这个文本摘要,发送方会使用自己的私钥对其进行加密,使用私钥加密后的文本摘要被称为数字签名(指纹)。
如何实现远端服务器的认证呢?其实每个合法服务器在建立时,都会去公证处(Certificate Authority,简称CA)给自己的公钥进行认证,公证处其实也是一台服务器,它会用自己的私钥给前来认证的服务器公钥以及它的一些相关信息一起加密,生成一套数字证书(Digital Certificate),这样认证过的服务器再给客户端发送数据时,只需要在发送数字签名的后面在加上自己的数字证书就行了,客户端的浏览器会有公证处服务器的公钥,用这个公钥可以将公证处的数字证书解密,拿到服务器真正的公钥,再用这个公钥对上面提到的数字签名进行解密,就可以拿到原来的文本摘要,再使用相同的哈希函数对原文信息进行处理,对比两个摘要就能确定,信息是否由真正的客户端发送,并且原文是否被修改过了。
作为一个多年的安全协议,HTTPS背后包含的安全内容十分丰富,文章也仅仅只是笔者自己的理解并用简陋的描述加以表达,作为分享以及后续的复习所用。如有描述不对或不全面的地方欢迎大家补充!