本篇繼續對於安全性測試話題,結合DVWA進行研習。
CSRF(Cross-site request forgery):跨站請求偽造
1. 跨站請求偽造攻擊
CSRF則通過偽裝成受信任用戶的請求來利用受信任的網站,誘使用戶使用攻擊性網站,從而達到直接劫持用戶會話的目的。
由於現在的主流瀏覽器比如火狐和谷歌,都傾向於使用單個進程來管理用戶會話(比如我們在FF和Chrome中,當要訪問一個新頁面時,通常是通過新增瀏覽器頁面來達到的,而不是新開一個瀏覽器客戶端進程),所以攻擊者就可以通過用戶新開的頁面來劫持用戶的已有cookie等關鍵信息。
常見的攻擊形式,是通過郵件,QQ等即時聊天工具,給被攻擊對象發送偽造鏈接。被攻擊者一旦訪問了該惡意鏈接,攻擊即生效。
DVWA的相應模塊中,使用一個密碼修改功能來展示了CSRF攻擊的可能性。
構造攻擊
我們觀察上述密碼修改功能所觸發的請求:
可以看到這個請求非常之簡單,所需傳遞的只有三個參數
- password_new
- password_conf
- change
那么如果攻擊者直接構造這樣一條請求交給被攻擊者去執行會怎么樣?
比如我構造這樣一條URL:
如果被攻擊者傻呵呵的點擊了,那么恭喜他,他的密碼就已經被我改成"attackerpw"了。
當然了,這個鏈接的攻擊意圖過於明顯了,攻擊者還可以通過偽裝一下這個鏈接,從而達到神不知鬼不覺的目的。
- 比如通過縮短鏈接服務:
- 比如通過構建一個帶有攻擊代碼的頁面,誘使被攻擊者訪問:
上圖看起來就是個不明所以的網頁?但實際上訪問到網頁的時候紅框部分的攻擊代碼就已經生效了!
2.CSRF的防御
下面我們看一看DVWA是如何防御跨站腳本偽造的:
Medium級別防御
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Checks to see where the request came from
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
// Get input
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Do the passwords match?
if( $pass_new == $pass_conf ) {
// They do!
$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass_new = md5( $pass_new );
// Update the database
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
// Feedback for the user
$html .= "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
$html .= "<pre>Passwords did not match.</pre>";
}
}
else {
// Didn't come from a trusted source
$html .= "<pre>That request didn't look correct.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
可以看到中級防御機制,關鍵在於以下部分:
即判斷請求來源,如果類似修改密碼的這種請求來自於未知第三方地址,那么則不執行修改邏輯。
這是不難繞過的,只需在攻擊頁面中加入HTTP_REFERER並使其與被攻擊server一致即可。
High級別防御
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Do the passwords match?
if( $pass_new == $pass_conf ) {
// They do!
$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass_new = md5( $pass_new );
// Update the database
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
// Feedback for the user
$html .= "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
$html .= "<pre>Passwords did not match.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
// Generate Anti-CSRF token
generateSessionToken();
?>
嘞了嘞了,耳熟能詳的Token他來了:
用戶每次訪問頁面,服務器會隨機生成一個Token,相當於用戶的身份牌。只有身份牌驗證通過,功能邏輯才執行。
Token是現在很流行的令牌機制,但是他也是一種比較簡單的機制,絕非無懈可擊。接口測試做的多的話應該能感受到,我們接口測試中經常會對Token做傳遞處理。
實際token就在網頁元素里面,如下圖所示
我們通過抓包、爬蟲、元素定位等方式完全可以獲取到,傳遞到攻擊頁面中 - 當然這會需要一定的代碼編寫量了!
Impossible級別
DVWA提供的最高級別防御機制,說穿了非常簡單,即修改密碼前,強制要求用戶輸入舊密碼。
如果不知道舊密碼,則無論怎樣也無法修改用戶密碼。
3. CSRF防御能力測試
結合着上述討論,同樣我們可以總結一下這一安全測試點的測試思路。
對於CSRF跨站腳本偽造攻擊我們可以做:
-
滲透性測試: 扮演攻擊者的角色,利用已知的攻擊手段嘗試跨站腳本偽造,比如自己構造請求和簡單的頁面進行攻擊。
-
運行時測試: 實際就是功能測試,我們可以通過驗證系統是否存在相應的防御功能:比如token機制,請求來源驗證等。
-
代碼審計: 了解了CSRF的幾個級別的防御機制,那么就可以通過代碼審計的方式來確定被測應用的后台邏輯有無相應防御機制。
還有一個問題在於,什么樣的系統功能點可能是CSRF攻擊的敏感區域呢?
從本文的例子中我們可以看到,當系統以一種簡單的請求方式實現某種功能時,CSRF就存在劫持用戶會話的可能性。那么這就要求測試人員能夠敏銳的發現類似的系統特性區域,並且予以判斷,是否有可能被會話劫持。
比如在一個購物平台中,訂單的提交流程如果過於簡單,一個簡單的get請求就能實現下單功能(從這一點來說,get請求就不應用於處理敏感操作,發現這種情況就是你測試的點!),那么你就可以摩拳擦掌,考慮來一次滲透測試了。
從這個角度來說,對於安全性測試,知識和技術的累積是一方面,而敏銳的思維能力特別是逆向思維能力更加重要!