網絡協議
參考
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的流程是先使用非對稱加密交換 對稱加密的密鑰. 之后使用對稱加密進行傳輸