跨站請求偽造
跨站請求偽造(CSRF)是一種允許攻擊者通過受害者發送任意HTTP請求的一類攻擊方法。此處所指的受害者是一個不知情的同謀,所有的偽造請求都由他發起,而不是攻擊者。這樣,很你就很難確定哪些請求是屬於跨站請求偽造攻擊。事實上,如果沒有對跨站請求偽造攻擊進行特意防範的話,你的應用很有可能是有漏洞的。
請看下面一個簡單的應用,它允許使用者購買鋼筆或鉛筆。介面上包含下面的表單:
CODE:
<form action="buy.php" method="POST"> <p> Item: <select name="item"> <option name="pen">pen</option> <optionname="pencil">pencil</option> </select><br /> Quantity: <input type="text" name="quantity"/><br /> <input type="submit" value="Buy" /> </p> </form>
一個攻擊者會首先使用你的應用以收集一些基本資料。例如,攻擊者首先訪問表單並發現兩個表單元素item及quantity,他也同時知道了item的值會是鉛筆或是鋼筆。
下面的buy.php程式處理表單的提交資訊:
CODE:
<?php session_start(); $clean = array(); if (isset($_REQUEST['item'] &&isset($_REQUEST['quantity'])) { /* Filter Input ($_REQUEST['item'],$_REQUEST['quantity']) */ if (buy_item($clean['item'],$clean['quantity'])) { echo '<p>Thanks for yourpurchase.</p>'; } else { echo '<p>There was a problem with yourorder.</p>'; } } ?>
攻擊者會首先使用這個表單來觀察它的動作。例如,在購買了一支鉛筆後,攻擊者知道了在購買成功後會出現感謝函息。注意到這一點後,攻擊者會嘗試通過訪問下面的URL以用GET方式提交資料是否能達到同樣的目的:
http://www.php.cn/
如果能成功的話,攻擊者現在就取得了當合法使用者訪問時,可以引發購買的URL格式。在這種情況下,進行跨站請求偽造攻擊非常容易,因為攻擊者只要引發受害者訪問該URL即可。
雖然有多種發起跨站請求偽造攻擊的方式,但是使用嵌入資源片的方式是最普遍的。為了理解這個攻擊的過程,首先有必要瞭解瀏覽器請求這些資源的方式。
當你訪問http://www.php.cn/ (圖2-1),你的瀏覽器首先會請求這個URL所標識的資源。你可以通過查看該頁的源檔案(HTML)的方式來看到該請求的返回內容。在瀏覽器解析了返回內容後發現了Google的標誌圖片。這個圖片是以HTML的img標籤表示的,該標籤的src屬性工作表示了圖片的URL。瀏覽器於是再發出對該圖片的請求,以上這兩次請求間的不同點只是URL的不同。
圖 2-1. Google的首頁
A CSRF attack can use an img tag to leverage thisbehavior. Consider visiting a web site with the following image identified inthe source:
根據上面的原理,跨站請求偽造攻擊可以通過img標籤來實現。考慮一下如果訪問包括下面的原始碼的網頁會發生什麼情況:
<imgsrc="http://store.example.org/buy.php?item=pencil&quantity=50" />
由於buy.php指令碼使用$_REQUEST而不是$_POST,這樣每一個只要是登入在store.example.org商店上的使用者就會通過請求該URL購買50支鉛筆。
跨站請求偽造攻擊的存在是不推薦使用$_REQUEST的原因之一。
完整的攻擊過程見圖2-2。
圖2-2. 通過圖片引發的跨站請求偽造攻擊
當請求一個圖片時,某些瀏覽器會改變要求標頭部的Accept值以給圖片類型以一個更高的優先權。需要採用保護措施以防止這種情況的發生。
你需要用幾個步驟來減輕跨站請求偽造攻擊的風險。一般的步驟包括使用POST方式而不是使用GET來提交表單,在處理表單提交時使用$_POST而不是$_REQUEST,同時需要在重要操作時進行驗證(越是方便,風險越大,你需要求得方便與風險之間的平衡)。
任何需要進行操作的表單都要使用POST方式。在RFC2616(HTTP/1.1傳送協議,譯註)的9.1.1小節中有一段描述:
“特別需要指出的是,習慣上GET與HEAD方式不應該用於引發一個操作,而只是用於擷取資訊。這些方式應該被認為是‘安全’的。客戶瀏覽器應以特殊的方式,如POST,PUT或DELETE方式來使使用者意識到正在請求進行的操作可能是不安全的。”
最重要的一點是你要做到能強制使用你自己的表單進行提交。儘管使用者提交的資料看起來象是你表單的提交結果,但如果使用者並不是在最近調用的表單,這就比較可疑了。請看下面對前例應用更改後的代碼:
CODE:
<?php session_start(); $token = md5(uniqid(rand(), TRUE)); $_SESSION['token'] = $token; $_SESSION['token_time'] = time(); ?> <form action="buy.php" method="POST"> <input type="hidden" name="token"value="<?php echo $token; ?>" /> <p> Item: <select name="item"> <option name="pen">pen</option> <optionname="pencil">pencil</option> </select><br /> Quantity: <input type="text" name="quantity"/><br /> <input type="submit" value="Buy" /> </p> </form>
通過這些簡單的修改,一個跨站請求偽造攻擊就必須包括一個合法的驗證碼以完全模仿表單提交。由於驗證碼的儲存在使用者的session中的,攻擊者必須對每個受害者使用不同的驗證碼。這樣就有效限制了對一個使用者的任何攻擊,它要求攻擊者擷取另外一個使用者的合法驗證碼。使用你自己的驗證碼來偽造另外一個使用者的請求是無效的。
該驗證碼可以簡單地通過一個條件運算式來進行檢查:
CODE:
<?php if (isset($_SESSION['token']) && $_POST['token'] == $_SESSION['token']) { /* Valid Token */ } ?>
你還能對驗證碼加上一個有效時間限制,如5分鐘:
CODE:
<?php $token_age = time() - $_SESSION['token_time']; if ($token_age <= 300) { /* Less than five minutes has passed. */ } ?>
通過在你的表單中包括驗證碼,你事實上已經消除了跨站請求偽造攻擊的風險。可以在任何需要執行操作的任何錶單中使用這個流程。
儘管我使用img標籤描述了攻擊方法,但跨站請求偽造攻擊只是一個總稱,它是指所有攻擊者通過偽造他人的HTTP請求進行攻擊的類型。已知的攻擊方法同時包括對GET和POST的攻擊,所以不要認為只要嚴格地只使用POST方式就行了。
以上就是PHP安全-跨站請求偽造的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!