理解HTTP消息頭 COOKIE、SESSION、POST(三)


客戶端發送的內容
這一次主要來觀察HTTP消息頭中客戶端的請求,從中找到一些有意思的內容。
 
1、HTTP_REFERER
 
寫兩個簡單的網頁:
a.htm:
<a href=b.htm>to page b</a>
b.htm:
haha

內容很簡單,就是網頁A中有一個到B的鏈接。把它們放到IIS上,並訪問網頁A,從中再點擊到B的鏈接,於是看到了B頁的“haha”。那么這兩次請求有什么不同嗎?觀察它們所發送的HTTP消息頭,最明顯的區別就是訪問B頁時比訪問A頁時多了一行:Referer: http://localhost/a.htm

這一行就表示,用戶要訪問的B頁是從A頁鏈接過來的。
服務器端要想取得這個值也是很容易的,以ASP為例,只需要寫一句 <% =Request.ServerVariables("HTTP_REFERER") %>就可以了。
一些網站通過HTTP_REFERER來做安全驗證,判斷用戶是不是從允許的頁面鏈接來的,而不是直接從瀏覽器上打URL或從其他頁面鏈接過來,這樣可以從一定程度上防止網頁被做非法使用。但從上述原理來看,想要騙過服務器也並不困難,只要手工構造輸入的HTTP消息頭就可以了,其他常用的手段還有通過HOSTS文件偽造域名等。
除了超鏈接以外,還有其他幾種方式會導致HTTP_REFERER信息被發送,如:
 1 內聯框架:     <iframe src=b.asp></iframe>
 2 框架集:        <frameset><frame src=b.asp></frameset>
 3 表單提交:     <form action=b.asp><input type=submit></form>
 4 SCRIPT引用:<script src=b.asp></script>
 5 CSS引用:     <link rel=stylesheet type=text/css href=b.asp>
 6 XML數據島:  <xml src=b.asp></xml>
 7 而以下形式不會發送HTTP_REFERER:
 8 script轉向:   <script>location.href="b.asp"</script>
 9 script開新窗口:<script>window.open("b.asp");</script>
10 META轉向:    <meta http-equiv="refresh" content="0;URL=b.asp">
11 引入圖片:      <img src=b.asp>

 2、COOKIE

COOKIE是大家都非常熟悉的了,通過它可以在客戶端保存用戶狀態,即使用戶關閉瀏覽器也能繼續保存。那么客戶端與服務器端是如何交換COOKIE信息的呢?沒錯,也是通過HTTP消息頭。首先寫一個簡單的ASP網頁:
1 <%
2 Dim i
3 i =  Request.Cookies("key")
4 Response.Write i
5 Response.Cookies("key") = "haha"
6 Response.Cookies("key").Expires = #2007-1-1#
7 %>

第一次訪問此網頁時,屏幕上一片白,第二次訪問時,則會顯示出“haha”。通過閱讀程序不難發現,屏幕上顯示的內容實際上是COOKIE的內容,而第一次訪問時還沒有設置COOKIE的值,所以不會有顯示,第二次顯示的是第一次設置的值。那么對應的HTTP消息頭應該是什么樣的呢?

第一次請求時沒什么不同,略過
第一次返回時消息內容多了下面這一行:
Set-Cookie: key=haha; expires=Sun, 31-Dec-2006 16:00:00 GMT; path=/

 很明顯,key=haha表示鍵名為“key”的COOKIE的值為“haha”,后面是這則COOKIE的過期時間,因為我用的中文操作系統的時區是東八區,2007年1月1日0點對應的GMT時間就是2006年12月31日16點。

第二次再訪問此網頁時,發送的內容多了如下一行:
Cookie: key=haha
它的內容就是剛才設的COOKIE的內容。可見,客戶端在從服務器端得到COOKIE值以后就保存在硬盤上,再次訪問時就會把它發送到服務器。發送時並沒有發送過期時間,因為服務器對過期時間並不關心,當COOKIE過期后瀏覽器就不會再發送它了。
如果使用IE6.0瀏覽器並且禁用COOKIE功能,可以發現服務器端的set-cookie還是有的,但客戶端並不會接受它,也不會發送它。有些網站,特別是在線投票網站通過記錄COOKIE防止用戶重復投票,破解很簡單,只要用IE6瀏覽器並禁用COOKIE就可以了。也有的網站通過COOKIE值為某值來判斷用戶是否合法,這種判斷也非常容易通過手工構造HTTP消息頭來欺騙,當然用HOSTS的方式也是可以欺騙的。
 
3、SESSION
 
HTTP協議本身是無狀態的,服務器和客戶端都不保證用戶訪問期間連接會一直保持,事實上保持連接是 HTTP1.1才有的新內容,當客戶端發送的消息頭中有“Connection:  Keep- Alive”時表示客戶端瀏覽器支持保持連接的工作方式,但這個連接也會在一段時間沒有請求后自動斷開,以節省服務器資源。為了在服務器端維持用戶狀態,SESSION就被發明出來了,現在各主流的動態網頁制做工具都支持SESSION,但支持的方式不完全相同,以下皆以ASP為例。
當用戶請求一個ASP網頁時,在返回的HTTP消息頭中會有一行:
Set-Cookie: ASPSESSIONIDCSQCRTBS=KOIPGIMBCOCBFMOBENDCAKDP; path=/

服務器通過COOKIE的方式告訴客戶端你的SESSIONID是多少,在這里是“KOIPGIMBCOCBFMOBENDCAKDP”,並且服務器上保留了和此SESSIONID相關的數據,當同一用戶再次發送請求時,還會把這個COOKIE再發送回去,服務器端根據此ID找到此用戶的數據,也就實現了服務器端用戶狀態的保存。所以我們用ASP編程時可以使用“session("name")=user”這樣的方式保存用戶信息。注意此COOKIE內容里並沒有過期時間,這表示這是一個當關閉瀏覽器時立即過期的COOKIE,它不會被保存到硬盤上。這種工作方式比單純用COOKIE的方式要安全很多,因為在客戶端並沒有什么能讓我們修改和欺騙的值,唯一的信息就是SESSIONID,而這個ID在瀏覽器關閉時會立即失效,除非別人能在你瀏覽網站期間或關閉瀏覽器后很短時間內知道此ID的值,才能做一些欺騙活動。因為服務器端判斷SESSION過期的方式並不是斷開連接或關閉瀏覽器,而是通過用戶手工結束SESSION或等待超時,當用戶關閉瀏覽器后的一段時間里SESSION還沒有超時,所以這時如果知道了剛才的SESSIONID,還是可以欺騙的。因此最安全的辦法還是在離開網站之前手工結束SESSION,很多網站都提供“Logout”功能,它會通過設置SESSION中的值為已退出狀態或讓SESSION立即過期從而起到安全的目的。

SESSION和COOKIE的方式各有優缺點。SESSION的優點是比較安全,不容易被欺騙,缺點是過期時間短,如果用過在超過過期時間里沒有向服務器發送任何信息,就會被認為超過過期了;COOKIE則相反,根據服務器端設置的超時時間,可以長時間保留信息,即使關機再開機也可能保留狀態,而安全性自然大打折扣。很多網站都提供兩種驗證方式相結合,如果用戶臨時用這台電腦訪問此訪問則需要輸入用戶名和密碼,不保存COOKIE;如果用戶使用的是自己的個人電腦,則可以讓網站在自己硬盤上保留COOKIE,以后訪問時就不需要重新輸入用戶名和密碼了。
 
4、POST

瀏覽器訪問服務器常用的方式有GET和POST兩種,GET方式只發送HTTP消息頭,沒有消息體,也就是除了要GET的基本信息之外不向服務器提供其他信息,網頁表單(FROM)的默認提交方式就是用GET方式,它會把所有向服務器提交的信息都作為URL后面的參數,如a.asp?a=1&b=2這樣的方式。而當要提交的數據量很大,或者所提交內容不希望別人直接看到時,應該使用POST方式。POST方式提交的數據是作為HTTP消息體存在的,例如,寫一個網頁表單:
<form method=post>
<input type=text name=text1>
<input type=submit>
</form>

訪問此網頁,並在表單中填入一個“haha”,然后提交,可以看到此次提交所發送的信息如下:

POST /form.asp HTTP/1.1
Accept: */*
Referer: http://localhost:8080/form.asp
Accept-Language: zh-cn
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; InfoPath.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
Host: localhost:8080
Content-Length: 10
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: key=haha; ASPSESSIONIDCSQCRTBS=LOIPGIMBLMNOGCOBOMPJBOKP
text1=haha

前面關鍵字從“GET”變為了“POST”,Content-Type變成了“application/x-www-form-urlencoded”,后面內容並無大變化,只是多了一行:Content-Length: 10,表示提交的內容的長度。空行后面是消息體,內容就是表單中所填的內容。注意此時發送的內容只是“Name=Value”的形式,表單上其他的信息不會被發送,所以想直接從服務器端取得list box中所有的list item是辦不到的,除非在提交前用一段script把所有的item內容都連在一起放到一個隱含表單域中。

如果是用表單上傳文件,情況就要復雜一些了,首先是表單聲明中要加上一句話:enctype='multipart/form-data',表示這個表單將提交多段數據,並用HTML:input type=file來聲明一個文件提交域。
表單內容如下:
<form method=post enctype='multipart/form-data'>
<input type=text name=text1>
<input type=file name=file1>
<input type=submit>
</form>

 

我們為text1輸入文字:hehe,為file1選擇文件haha.txt,其內容為“ABCDEFG”,然后提交此表單。提交的完全信息為:
POST /form.asp HTTP/1.1
Accept: */*
Referer: http://localhost:8080/form.asp
Accept-Language: zh-cn
Content-Type: multipart/form-data; boundary=---------------------------7d62bf2f9066c
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; InfoPath.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
Host: localhost:8080
Content-Length: 337
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: key=haha; ASPSESSIONIDCSQCRTBS=LOIPGIMBLMNOGCOBOMPJBOKP
-----------------------------7d62bf2f9066c
Content-Disposition: form-data; name="text1"
hehe
-----------------------------7d62bf2f9066c
Content-Disposition: form-data; name="file1"; filename="H:\Documents and Settings\Administrator\桌面\haha.txt"
Content-Type: text/plain
ABCDEFG
-----------------------------7d62bf2f9066c--

 顯然這個提交的信息要比前述的復雜很多。Content-Type變成了“multipart/form-data”,后面還多了一個boundary,此值是為了區分POST的內容的區段用的,只要在內容中遇到了此值,就表示下面要開始一個新的區段了,每個區段的內容相對獨立。如果遇到的是此值后面連着兩個減號,則表示全部內容到此結束。每個段也分為段頭和段體兩部分,用空行隔開,每段都有自己的類型和相關信息。如第一區段是text1的值,它的名稱是“text1”,值為“hehe”。第二段是文件內容,段首里表明了此文件域的名稱“file1”和此文件在用戶磁盤上的位置,后面就是文件的內容。

如果我們想要自己寫一個上傳文件組件來接收HTML表單傳送的文件數據,那么最核心的任務就是解析此數據包,從中取得需要的信息。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM