> 本文鏈接:[Web開發新人培訓系列(一)——協議](http://rapheal.sinaapp.com/2014/07/11/webdev-protocol/ "本文固定鏈接 http://rapheal.sinaapp.com/2014/07/11/webdev-protocol/")
> 來源:[拉風的博客](http://rapheal.sinaapp.com/)
## 前言
近期給實習生介紹一些基本概念,順便梳理一下文章,方便之后可重復利用。
從事Web開發必須要了解從瀏覽器輸入URL后到頁面加載完之后的整個大概的流程,當然可以就某些點細化的研究下去,這一過程涉及到:HTTP/HTTPS協議、代理/反向代理、Web server、CGI、瀏覽器端的知識(HTML+Javascript+CSS)
只有把整個流程都梳理清楚,才有能力去討論其他問題,例如如何去優化Web性能、如何利用工程化提高效率、在某些業務場景的技術方案取舍等。
本篇文章會以一個例子介紹計算機網絡基本工作的過程,最終再詳細介紹HTTPS是如何解決網絡包加密問題。繪說明圖找不到相關好的工具,所以文章引用了幾張來自[《圖解TCP/IP》](http://book.douban.com/subject/24737674/)的圖
## 快遞
我們來看一個常常出現的場景:
Alice在淘寶買了一件衣服,我們輸入收貨地址收貨人付款后,賣家Bob幫我們把衣服打包放進紙箱里,貼上一個快遞單:
> *寄件人:Bob
> 地址:北京市朝陽區朝陽路1號
> 收件人:Alice
> 地址:廣東省廣州市新港中路197號C公司*
快遞公司D派人去Bob家收到包裹,接著整理了公司的包裹,分批往個個城市送貨。由于D公司沒有直達廣州的貨運車,因此這些包裹先隨著去上海的車到達上海的中轉站之后,再把要發往廣州的包裹搬到另一輛車發往廣州中轉站。
快遞員Rapheal把快遞送到C公司,快遞中心的前臺mm幫Alice簽收后轉交給了Alice。
## 計算機網絡
以上的場景幾乎就是計算機網絡的場景了,我們做個類比(類比只到網絡層):
> 朝陽路1號 == 北京某臺計算機ip地址
> 新港中路197號 == 廣州某臺計算機的ip地址
> 快遞單 == 包頭
> 衣服 == 包體
> 快遞公司的中轉站 == 交換機/路由器
> Bob == 北京某臺計算機的進程B(端口)
> Alice == 廣州C公司局域網里邊的某臺計算機的進程A(端口)
> C公司的快遞中心 == C公司局域網的網關
[](http://rapheal-wordpress.stor.sinaapp.com/uploads/2014/07/1.png)
上述類比并不嚴謹,這里僅僅簡單解釋一下網絡里邊發送/接收包的大概過程跟概念:
1. 首先接入網絡的計算機/路由器都要有一個ip地址,例如Alice的ip地址是120.0.0.2,Bob的ip地址是150.0.0.2。
[](http://rapheal-wordpress.stor.sinaapp.com/uploads/2014/07/2.png)圖片來自[《圖解TCP/IP》](http://book.douban.com/subject/24737674/)
2. Alice發送HTTP數據給Bob,實際上是Alice機器上的的進程a發送數據,Bob機器上的的進程b在接收數據,因為真正想要數據的進程才是最終應用層。
3. 進程a發送數據是通過socket發出去,socket需要綁定一個端口,用于區分計算機內的不同進程。進程a使用的socket綁定了2048端口,進程b使用的socket綁定了4096端口。
4. 所以Alice跟Bob通信實際上等價于:120.0.0.2的2048端口發送數據到150.0.0.2的4096端口
Alice的計算機發送數據:
1. 進程a先把數據打包好,貼上單(HTTP協議頭部):我來自Chrome瀏覽器; 我要Bob的index.html
2. 再打包貼上單(TCP協議頭部):發送端口2048,接收端口4096
3. 繼續包裝貼上(IP協議頭部):發送地址:120.0.0.2,接收地址:150.0.0.2。
4. 這個包裹丟給快遞公司(網絡),接著就是快遞公司負責送往目的地Bob的機器了。
路由器轉發數據包:
1. 快遞公司中轉站負責把當前的包裹傳遞到下一個快遞中心,這也是路由器轉發的原理
2. 在北京的路由器先把包轉到了離他近一點的天津,天津再轉發到上海,上海再轉發到廣州,廣州在轉給海珠區的郵件中心,最后再到達目的地。
以下圖片來自[《圖解TCP/IP》](http://book.douban.com/subject/24737674/):
[](http://rapheal-wordpress.stor.sinaapp.com/uploads/2014/07/3.png)
Bob的計算機接收數據包:
1. 計算機收到來自150.0.0.2的包,解開后發現原來是送到端口4096去的,于是乎把包裹才開丟給了進程b(進程b使用的socket綁定了4096端口)
2. 進程b拿到數據之后就可以做自己的操作了,如果他要回包給Alice,那就按照Alice打包的過程,生成一個包發送出去。
我們經常會聽到計算機網絡分成7層,可以這么理解:Bob把衣服裝個箱子,箱子上貼個單說Alice收,接著再拿一個箱子裝著,上邊又貼了一個單寫著騰訊公司收,最后再裝一個箱子貼著廣州新港中路197號收。這里的例子分成了三層,網絡中運輸的數據包也是這樣的結構,IP包里邊裝著TCP包,TCP包里邊裝著HTTP包,每個協議層都有自己的包頭(單)跟包體(箱子)。
以下圖片來自[《圖解TCP/IP》](http://book.douban.com/subject/24737674/):
[](http://rapheal-wordpress.stor.sinaapp.com/uploads/2014/07/4.png)
## 協議
協議的目的就是讓通信的雙方知道當前這個數據包是怎么樣一個組成格式,一般:包頭就是雙方約定好的一些信息,包體就是這次通信傳輸的數據。
Alice拿到快遞之后從快遞單(包頭)就可以知道是Bob發給他的衣服,然后拆開之后就拿到衣服(包體),從而完成了此次通信。所以協議本身并沒有那么復雜難解:協議 == 包頭 + 包體。
協議繁瑣的地方在于,計算機怎么識別每一層的包頭,識別后做什么操作。例如路由器要識別出IP,然后決定當前數據包往那個地方轉發;瀏覽器要識別出包頭的Content-type,來決定當前這個包體是圖片還是HTML。
剛剛說了包體其實就是數據本身,數據本身就沒什么好理解的了,數據是什么,它就是什么,所以理解每一層的協議就在于理解其包頭的含義。所以接下來我以HTTP頭來介紹HTTP協議。
## HTTP協議
剛剛說到其實協議就是讓瀏覽器跟服務器互相知道他們要什么,因此瀏覽器發給服務器的包就要帶著一些用戶操作信息,服務器發給瀏覽器的包就要帶著用戶想要的數據信息。
當瀏覽器輸入一個URL,相當于瀏覽器發起一個HTTP請求包出去,瀏覽器會把一些自身信息以及用戶操作的信息寫在包頭上帶到服務器,這樣服務器才知道你在用什么瀏覽器想請求它的什么資源。
HTTP請求包的常見包頭如下(紫色為說明文字):
* GET / HTTP/1.1
*獲取的路徑以及HTTP版本*
* Host: www.qq.com:8080
*服務器主機名字&端口號*
* Connection: keep-alive
*用于長連*
* User-Agent: Chrome/35.0.1916.153
*瀏覽器基本信息*
* Accept-Encoding: gzip,deflate,sdch
*告訴服務器支持的編碼*
* Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,nl;q=0.4,zh-TW;q=0.2
*告訴服務器優先支持的語言*
* Cookie: id=1;username=raphealguo;
*解決HTTP無狀態重要的Cookie,里邊是key=value對*
緊接著服務器收到HTTP請求,經過CGI處理之后回復一個HTTP響應包,服務器需要告訴你包里邊是什么(Content-Type),包里邊有多少東西(Content-Length),服務器版本是什么等等。
HTTP響應包常見的包頭如下:
* HTTP/1.1 200 OK
*HTTP版本以及狀態碼*
* Server: nginx/1.4.1
*服務器版本*
* Date: Mon, 30 Jun 2014 09:44:10 GMT
*回包時間*
* Content-Type: text/html; charset=UTF-8
*包里邊的類型*
* Content-Length: 14534
*包體的長度*
* Connection: keep-alive
*用于長連*
* Cache-Control: no-cache, must-revalidate
*用于緩存控制*
具體的HTTP協議的細節推薦閱讀《HTTP權威指南》。
## HTTPS協議
北京的Bob發了一個快遞到廣州的Alice,途中經過了上海,上海快遞中心出現了一個黑客H,他偷偷打開了Bob給Alice的快遞,然后偷偷把里邊的衣服剪爛,再按照原樣包裝好發往廣州,可以看到對于這樣簡單包裝的傳輸在中途是可以偷偷修改里邊的東西。
HTTP的數據包是明文傳輸,也即是如果中途某個黑客嗅探到這個HTTP包,他可以偷偷修改里邊包的內容,至于Bob跟Alice是互相不知道這個動作的,因此我們必須要有一個方案來防止這種不安全的篡改行為,有個方法就是加密!
### 非對稱加密
Bob將衣服放到一個保險箱里邊鎖起來,他打了個電話告訴Alice保險箱開柜密碼是1234,而黑客H不知道密碼,所以他看不到保險箱里邊的東西,Alice收到快遞后用預先溝通好的密碼就可以打開保險箱了。
這里保護的手段就是Bob對物品進行加密,同時給了告訴Alice解密的方法!
那如果現在要求Bob的密碼只能通過快遞傳給Alice呢?如果Bob直接傳密碼給Alice,H如果嗅探到這個快遞,那H也知道密碼了,這就無法保護快遞的安全性了。因此還需要有個方案,讓Bob能夠告訴Alice密碼的同時,H又無法查看到Bob跟Alice通信的數據。
非對稱加密在這個時候就發揮作用了,來看看怎么回事:Bob擁有兩把鑰匙,一把叫做公鑰,一把叫做私鑰。公鑰是公開讓全社會都知道,沒關系,Bob告訴所有人,你們要傳遞數據給我的時候請先用這個密鑰去加密一下你們的數據,加密后的數據只能通過Bob私自藏著的私鑰才能解密。
回到剛剛例子,Bob先發給保險柜(Bob公鑰)給Alice,接著Alice把自己的保險柜(Alice公鑰)放到Bob的保險柜里邊發還給Bob,接著Bob拿到Alice的回包后,用自己的私鑰解開了外層保險柜,拿到了里邊Alice保險柜。此時Alice跟Bob都有了各自的公鑰,接著只要保證每次互相傳遞數據的時候,把數據放在對方的保險柜里邊即可,這樣無論如何,H都無法解開保險柜(因為只有各自的私鑰才能解開各自的保險柜)。
### HTTPS隧道
為了使得HTTP傳輸的安全性,HTTPS就誕生了,同剛剛Bob跟Alice通信一樣,HTTPS會有如下的過程:
1. 客戶端先跟服務器做一次SSL握手,也就是剛剛Bob跟Alice交換公鑰的過程。
2. 此時客戶端跟服務器都有了各自的公鑰,這時他們中間相當于有了一條安全的HTTPS隧道。
3. 客戶端要發送請求時,采用服務器給的公鑰對請求包進行加密,然后發出去。
4. 服務器收到請求后,使用自己的私鑰解開了這個請求包得到其內容。
5. 服務器響應的時候,采用客戶端給的公鑰進行加密,然后發還給客戶端。
6. 客戶端收到響應后,使用自己的私鑰解開響應包得到其內容。
7. 結束的時候,雙方關閉SSL隧道,丟掉上次交換的公鑰。
以下圖片來自[《圖解TCP/IP》](http://book.douban.com/subject/24737674/):
[](http://rapheal-wordpress.stor.sinaapp.com/uploads/2014/07/5.png)
### HTTPS安全?
那HTTPS是不是任何情況都安全的呢?從剛剛的例子中我們可以看到HTTPS其實是一個端到端的安全保障,HTTPS保障的是你這個數據包在傳輸的過程中無法被查看,但是這個數據包也是可以被黑客偷偷丟掉或者毀壞。來看看HTTPS在什么情況下是不安全的:
1. 私鑰被竊取,如果這個被爆了,那HTTPS肯定就不安全了
2. HTTPS是無法保證端內安全,也即是如果你的電腦已經被病毒侵入,病毒可以監聽到你開鎖的時候,也可以知道你的私鑰,那這份數據怎么樣都保證不了安全性了。
3. HTTPS下的頁面引用了HTTP下的資源,例如HTTPS下的頁面引用了HTTP下的Javascript文件,此時如果這個Javascript文件被篡改了,那通過這個JS文件就可以在執行的時候獲取到整個網頁內容,在這種情況下,某些瀏覽器會阻止你去加載這些非HTTP資源,有些則會提示錯誤或者給出警告信息。