PHP安全-跨站請求偽造
跨站請求偽造(CSRF)是一種允許攻擊者通過受害者發(fā)送任意HTTP請求的一類攻擊方法。此處所指的受害者是一個不知情的同謀,所有的偽造請求都由他發(fā)起,而不是攻擊者。這樣,很你就很難確定哪些請求是屬于跨站請求偽造攻擊。事實上,如果沒有對跨站請求偽造攻擊進行特意防范的話,你的應(yīng)用很有可能是有漏洞的。
請看下面一個簡單的應(yīng)用,它允許用戶購買鋼筆或鉛筆。界面上包含下面的表單:
CODE:
<form action='buy.php' method='POST'>
<p>
Item:
<select name='item'>
<option name='pen'>pen</option>
<option name='pencil'>pencil</option>
</select><br />
Quantity: <input type='text' name='quantity' /><br />
<input type='submit' value='Buy' />
</p>
</form>
一個攻擊者會首先使用你的應(yīng)用以收集一些基本信息。例如,攻擊者首先訪問表單并發(fā)現(xiàn)兩個表單元素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 your purchase.</p>’;
}
else
{
echo ’<p>There was a problem with your order.</p>’;
}
}
?>
攻擊者會首先使用這個表單來觀察它的動作。例如,在購買了一支鉛筆后,攻擊者知道了在購買成功后會出現(xiàn)感謝信息。注意到這一點后,攻擊者會嘗試通過訪問下面的URL以用GET方式提交數(shù)據(jù)是否能達(dá)到同樣的目的:
http://store.example.org/buy.php?item=pen&quantity=1
如果能成功的話,攻擊者現(xiàn)在就取得了當(dāng)合法用戶訪問時,可以引發(fā)購買的URL格式。在這種情況下,進行跨站請求偽造攻擊非常容易,因為攻擊者只要引發(fā)受害者訪問該URL即可。
雖然有多種發(fā)起跨站請求偽造攻擊的方式,但是使用嵌入資源如圖片的方式是最普遍的。為了理解這個攻擊的過程,首先有必要了解瀏覽器請求這些資源的方式。
當(dāng)你訪問http://www.google.com (圖 2-1),你的瀏覽器首先會請求這個URL所標(biāo)識的資源。你可以通過查看該頁的源文件(HTML)的方式來看到該請求的返回內(nèi)容。在瀏覽器解析了返回內(nèi)容后發(fā)現(xiàn)了Google的標(biāo)志圖片。這個圖片是以HTML的img標(biāo)簽表示的,該標(biāo)簽的src屬性表示了圖片的URL。瀏覽器于是再發(fā)出對該圖片的請求,以上這兩次請求間的不同點只是URL的不同。
圖 2-1. Google的首頁
A CSRF attack can use an img tag to leverage this behavior. Consider visiting a web site with the following image identified in the source:
根據(jù)上面的原理,跨站請求偽造攻擊可以通過img標(biāo)簽來實現(xiàn)。考慮一下如果訪問包括 下面的源代碼的網(wǎng)頁會發(fā)生什么情況:
<img src='http://store.example.org/buy.php?item=pencil&quantity=50' />
由于buy.php腳本使用$_REQUEST而不是$_POST,這樣每一個只要是登錄在store.example.org商店上的用戶就會通過請求該URL購買50支鉛筆。
跨站請求偽造攻擊的存在是不推薦使用$_REQUEST的原因之一。
完整的攻擊過程見圖2-2。
圖2-2. 通過圖片引發(fā)的跨站請求偽造攻擊
當(dāng)請求一個圖片時,某些瀏覽器會改變請求頭部的Accept值以給圖片類型以一個更高的優(yōu)先權(quán)。需要采用保護措施以防止這種情況的發(fā)生。
你需要用幾個步驟來減輕跨站請求偽造攻擊的風(fēng)險。一般的步驟包括使用POST方式而不是使用GET來提交表單,在處理表單提交時使用$_POST而不是$_REQUEST,同時需要在重要操作時進行驗證(越是方便,風(fēng)險越大,你需要求得方便與風(fēng)險之間的平衡)。
任何需要進行操作的表單都要使用POST方式。在RFC 2616(HTTP/1.1傳送協(xié)議,譯注)的9.1.1小節(jié)中有一段描述:
“特別需要指出的是,習(xí)慣上GET與HEAD方式不應(yīng)該用于引發(fā)一個操作,而只是用于獲取信息。這些方式應(yīng)該被認(rèn)為是‘安全’的。客戶瀏覽器應(yīng)以特殊的方式,如POST,PUT或DELETE方式來使用戶意識到正在請求進行的操作可能是不安全的。”
最重要的一點是你要做到能強制使用你自己的表單進行提交。盡管用戶提交的數(shù)據(jù)看起來象是你表單的提交結(jié)果,但如果用戶并不是在最近調(diào)用的表單,這就比較可疑了。請看下面對前例應(yīng)用更改后的代碼:
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>
<option name='pencil'>pencil</option>
</select><br />
Quantity: <input type='text' name='quantity' /><br />
<input type='submit' value='Buy' />
</p>
</form>
通過這些簡單的修改,一個跨站請求偽造攻擊就必須包括一個合法的驗證碼以完全模仿表單提交。由于驗證碼的保存在用戶的session中的,攻擊者必須對每個受害者使用不同的驗證碼。這樣就有效的限制了對一個用戶的任何攻擊,它要求攻擊者獲取另外一個用戶的合法驗證碼。使用你自己的驗證碼來偽造另外一個用戶的請求是無效的。
該驗證碼可以簡單地通過一個條件表達(dá)式來進行檢查:
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. */
}
?>
通過在你的表單中包括驗證碼,你事實上已經(jīng)消除了跨站請求偽造攻擊的風(fēng)險。可以在任何需要執(zhí)行操作的任何表單中使用這個流程。
盡管我使用img標(biāo)簽描述了攻擊方法,但跨站請求偽造攻擊只是一個總稱,它是指所有攻擊者通過偽造他人的HTTP請求進行攻擊的類型。已知的攻擊方法同時包括對GET和POST的攻擊,所以不要認(rèn)為只要嚴(yán)格地只使用POST方式就行了。
相關(guān)文章:
1. jsp實現(xiàn)登錄驗證的過濾器2. ASP中實現(xiàn)字符部位類似.NET里String對象的PadLeft和PadRight函數(shù)3. jsp+servlet簡單實現(xiàn)上傳文件功能(保存目錄改進)4. 微信開發(fā) 網(wǎng)頁授權(quán)獲取用戶基本信息5. JavaWeb Servlet中url-pattern的使用6. asp批量添加修改刪除操作示例代碼7. 詳解瀏覽器的緩存機制8. HTML5 Canvas繪制圖形從入門到精通9. css代碼優(yōu)化的12個技巧10. msxml3.dll 錯誤 800c0019 系統(tǒng)錯誤:-2146697191解決方法
