1. 程式人生 > 其它 >第八次實驗 獲取網頁引數

第八次實驗 獲取網頁引數

實驗目的

瞭解http協議的GET方法和POST方法,以及他們的區別,應用場景,HTTP狀態碼含義,如何向服務端提交引數,服務端如何接受使用者提交的引數。

實驗環境

一臺windows 7、安裝wampserver。

實驗步驟一

百度大家應該都用過,我們可以在百度查詢一些想要了解的資訊。但是我們需要告訴它,我們想要查詢什麼內容,這就需要我們告訴伺服器我們需要查詢與什麼相關的內容,所以我們需要在搜尋框輸入我們想要查詢的資訊的關鍵字,這裡輸入的關鍵字就是給百度的伺服器傳入引數(以後簡稱傳參),百度的伺服器在接收了這個引數後,進行了一些處理,然後返回相關資料,比如我們輸入“網安實驗室”,百度就會給我們返回與“網安實驗室”相關度較高的網頁,如圖:

可以看到,輸入的關鍵字到了URL裡面,像這種給伺服器傳參的方法,就是HTTP協議中的GET方法,在GET方法中,查詢字串(名稱/值)是在URL中傳送的。其格式如下:

/demo.php?name1=value1&name2=value2

一般來說,只用來向伺服器獲取資訊,不向伺服器傳敏感資料。

要證明這是GET方法也很簡單,可以在網頁的任何位置滑鼠右擊,然後選擇檢視元素。然後點選網路。

此時會看到很多請求,每個請求都顯示了狀態碼(即狀態一欄)、請求方法、請求檔案、請求域名、請求原因、請求型別等。

在這些請求中,找到請求原因是document,請求型別是html的一行,這行就是搜尋所發起的請求,後面的一些請求都是在請求完成後發起的,因為第一次請求完以後,有些如圖片瀏覽器需要重新拉取。

我們來看下php是如何獲取使用者通過瀏覽器傳過去的引數的。

在PHP裡面,有許多預定義的變數都是“超全域性的”,這代表他們在一個指令碼的內部的任何地方都可以使用,即使實在函式或者方法中,要訪問這些“超全域性變數”也無需使用global $var;這些超全域性變數是:

$GLOBALS

$_SERVER

$_GET

$_POST

$_FILES

$_COOKIE

$_SESSION

$_REQUEST

$_ENV

本步驟中先了解$_GET。

$_GET這個超全域性變數是一個數組,它儲存了通過 URL 引數傳遞給當前指令碼的引數的值,也就是說,所有通過get方法傳過來的引數,都可以通過這個超全域性變數來取得引數對應的值,要取得指定引數的值,可以通過引數名來索引$_GET這個陣列。

在實驗環境中,已經配置了sqli.com域名,現在通過程式碼來看下如何獲取使用者傳過來的引數。在實驗環境中開啟火狐瀏覽器,訪問sqli.com/get.php?id=12&name=admin。以後涉及到需要訪問sqli.com的步驟,都在試驗機中完成。

這裡的?表示從?後面開始是傳引數,當我們需要同時向伺服器傳多個引數的時候,需要使用&來連線多個引數。這裡代表往伺服器傳了id、name這2個引數,值分別是12、admin。看程式碼更能直觀地理解含義。該頁面原始碼如下:

它的作用僅僅只是輸出使用者通過get方法傳過來的id和name這2個引數的值。

可以看到頁面輸出的就是我們傳過去的值,看別人的漏洞分析文章的時候,可能會經常看到說某某變數可控,在這裡,我們通過GET方法傳過去的值就是可控的,可控指的是:我們可以控制某個變數的值。

通過GET方法我們可以給伺服器傳多個值,比如還可以在後面加一些引數。現在我們給該頁面傳一些其他引數,然後列印下這個全域性變數。

首先進入C:\wamp\www\sqli目錄,開啟get.php,把前面輸出的程式碼註釋掉(在英文輸入狀態下,在每行的最前面輸入2個 / 表示註釋掉這一行),把最後一行取消註釋。改完以後的程式碼如圖:

var_dump用來列印變數的相關資訊,包括表示式的型別與值。

然後訪問sqli.com/get.php?id=12&name=admin&user=test&home=dd,訪問後輸出如下圖:

可以看到,該變數的型別是array,也就是陣列。在“=>”左邊的是這個陣列的鍵,右邊的是值。

需要注意的是,我們傳過去的引數,伺服器會原封不動地接受引數,如圖:

如果我們傳過去的值裡面有一些特殊字元,比如單引號、雙引號等服務端沒有任何處理,直接拼接sql語句的話,則會導致SQL注入、XSS等安全漏洞。想深入瞭解的可以學習“SQL注入原理與實踐”、“XSS跨站指令碼攻擊原理與實踐”等實驗。

實驗步驟二

除了上面講到的用GET方法給伺服器傳參,還有一種常用的傳參方法,那就是POST。如果通過POST方法傳參,那麼引數是在http請求的請求體中,並且是經過URL encode編碼的。

在登入的地方一般都是用POST方法,前面已經說過,如果包含敏感資訊,就應該用POST方法。

還是開啟百度,點選右上角的登入,彈出登入視窗:

在輸入框中隨便輸入一些內容,如果需要驗證碼,需要輸入正確的驗證碼。 然後點選登入。

提示輸入的賬號或密碼有誤,但是url並沒有改變,也就是說並沒有通過GET方法給伺服器傳引數,那麼伺服器是如何知道我們輸入的賬號和密碼的呢?

這裡雖然沒有通過get方法傳參,但是它通過post方法給伺服器傳參了。按F12或者滑鼠右擊頁面選擇檢視元素進入開發人員工具,進入網路面板。再次輸入賬號密碼以及驗證碼,在點選登入之前點選左上方的垃圾桶圖示清空內容。 可以看到,第一個請求就是post請求。點選post請求的這一行。會彈出這個請求的詳情。

可以看到給伺服器傳了很多引數,而我們輸入的只有2個,加上驗證碼也就三個引數,這說明百度自動帶入了一些其他引數,在裡面可以看到username、password這2個引數,從引數名我們就可以猜出分別代表我們輸入的使用者名稱和密碼,但是輸入的密碼明明只有幾個字元,為什麼到這裡就變成了一串很長的毫無規律的字串呢? 這說明我們輸入的密碼是被加密後才被髮送百度伺服器。

那麼PHP又是如何接受通過POST方法傳過去的引數的呢?

還記得前面介紹的PHP中的超全域性變數嗎?其中就有一個$_POST的超全域性變數,它也是一個數組,它儲存了使用者通過POST方法傳過去的引數。

檢視c:\wamp\www\sqli目錄下的post.php的原始碼,如下圖:

關鍵程式碼已經寫了註釋,頁面很簡單,獲取使用者的輸入內容然後原樣輸出到頁面,當然在實際開發中,不是這麼簡單,一般都是獲取使用者的輸入後,儲存到資料庫中,當然在儲存之前還要先判斷該使用者是否已經註冊,如果資料庫中已經存在一個同樣的使用者名稱,則會提醒使用者該使用者已註冊。

那麼,又是如何得知陣列的哪個鍵對應使用者輸入的輸入框呢? 仔細檢視上面的html的程式碼。

每個輸入框都有個name屬性,我們可以通過這個name屬性的值來索引使用者的輸入框內容,比如,在使用者名稱的輸入框輸入:test,那麼可以通過$_POST['username'] 來獲取該輸入框的值,也就是說,$_POST['username']的值為test。

可能細心的朋友已經看出來了,這明明是一個php頁面,怎麼也可以寫html程式碼呢? 因為在php檔案中,php 直譯器只有在遇到 <?php 的時候,才開始把後面的程式碼當成php程式碼處理,不在<?php ?>內的程式碼不處理,直接原樣輸出。<?php 是php的開始標記,告訴php直譯器,在遇到結束標記前,都是php的程式碼。?> 是 php的結束標記。

瀏覽器開啟訪問http://sqli.com/post.php.

為了更方便地瞭解post傳參的位置,我們使用抓包工具來抓包,抓包工具通過修改瀏覽器代理的方法,來攔截瀏覽器傳送的資料包,攔截資料包以後,可以進行修改資料包,丟棄資料包等操作。

機器已經安裝好fiddler,點選桌面fiddler圖示開啟fiddler。

然後進入瀏覽器設定。

進入瀏覽器代理設定頁面。

選擇使用系統代理設定,點選確認。

重新訪問http://sqli.com/post.php,此時可以在fiddler看到我們剛才的請求,由於我們沒有設定攔截請求,所以預設直接允許請求了。

還有其他不相關的一些請求,可以不用管。點選這個請求,檢視這個請求的原始請求。如果你是重新輸入的url訪問,或者之前沒有點選提交,那麼你可以看到這個請求是get請求而不是post,其中紅框標記中的內容為http請求頭。

其中第一行的GET說明這次請求是GET請求,後面的url表示本次請求的路徑,最後面的HTTP/1.1說明HTTP 1.1版本。

由於我們沒有在頁面的輸入框中填內容,所以該請求是GET請求,如果在輸入框中填了內容,那麼請求方式就會變成POST。

內容隨便,填好以後先不點提交,在fiddler中,發現有多餘的請求,先把這些請求清空掉。點選上方的X,然後選擇remove_all。就會清空所有記錄,然後再點選提交。

可以看到,頁面輸入了我們輸入的內容,同時fiddler中,也顯示了我們本次請求,點選這個請求,檢視該請求詳情,同樣來到raw的介面檢視原始的http請求。 可以看到,請求方法變成了POST,此時,在下方,比上次GET請求明顯多了一些資料。

這些資料就是我們通過POST方法傳給伺服器的引數,這些資料所在的位置就是HTTP請求體中,HTTP請求頭和HTTP請求體用空行分割。

同樣的,POST請求的資料,也需要經過url編碼,可以在頁面輸入中文測試,填寫如下內容: 點選提交後,來到fiddler檢視這次請求的POST引數。

很明顯的經過了URL編碼,伺服器在接受這些引數值的時候,會自動URL解碼。所以輸出的字元還是我們在輸入框中輸入的內容。

同樣,如果服務端在接受使用者輸入的引數後,沒有進行處理,就直接拼接SQL語句進行查詢就可能會引起安全漏洞,比如最常見的SQL注入和XSS。原理可以參考“SQL注入原理與實踐”、“XSS跨站指令碼攻擊原理與實踐”等實驗。

通過前面的實驗,可以知道,GET方法和POST方法的區別,主要就是傳參的地方不同,GET方法通過URL傳參,POST方法在請求體中傳參。在實際應用中,GET方法多用來向伺服器發起請求獲取資源,而POST方法多用在需要修改資源的地方,以及一些敏感資訊使用POST方法,並沒有什麼規範強制要求在登入處使用POST方法。但是我們應該使用POST方法而不用GET方法,因為POST方法相對來說,比GET方法私密,因為引數不會顯示URL而在請求體中,使用者看不到請求體。有些請求必須使用POST方法。比如向伺服器傳檔案等。因為伺服器明確規定要求使用POST方法。