CSRF攻擊簡介
關鍵處理中引入的安全隱患:
Web應用中,用戶登錄後執行的操作中有些處理一旦完成就無法撤銷。比如用戶使用信用卡支付、從用戶的銀行賬號轉賬、發送郵件、更改密碼或郵箱地址等都是關鍵處理的典型案例。
關鍵處理中如果存在安全隱患,就會產生名爲**跨站請求僞造(Cross-Site Request Forgeries,簡稱CSRF)**的漏洞。
CSRF介紹:
在執行關鍵處理前,需要確認該請求是否確實由用戶自願發起。如果忽略了這個步驟,就可能出現很大的問題,比如用戶只是流量惡意網站,瀏覽器就擅自執行關鍵處理等。
引發上述問題的安全隱患爲稱爲跨站請求僞造(CSRF)漏洞,而針對CSRF漏洞進行的攻擊就是CSRF攻擊。
Web應用存在CSRF漏洞時就可能會遭受如下攻擊:
- 使用用戶的賬號購物
- 刪除用戶賬號
- 使用用戶的賬號發佈帖子
- 更改用戶密碼或郵箱地址等。
CSRF漏洞造成的影響僅限於應用的關鍵處理被惡意使用,而像用戶的個人信息等就無法通過CSRF攻擊竊取。
因此,爲了預防CSRF漏洞,就需要在執行關鍵處理前確認請求確實是由用戶自願發起的。
CSRF漏洞總覽:
產生地點: 以下任一網站上執行關鍵處理的頁面。
- 僅使用Cookie進行會話管理的網站。
- 僅依靠HTTP認證、SSL客戶端證書、手機的移動ID來識別用戶的網站。
影響範圍:
- 存在CSRF漏洞的頁面。
影響類型:
- 以受害用戶的權限執行關鍵處理。如購買商品、發佈帖子、更改密碼等。
影響程度:
- 中~大
用戶參與程度:
- 需要–>點擊惡意鏈接、瀏覽惡意網站等。
對策概要:
- 執行關鍵處理前,確認是正規用戶發起的請求。
兩種典型的CSRF攻擊模式:
CSRF漏洞實施的兩種典型的攻擊模式。
- 輸入–執行 這種簡單模式的攻擊。
- 中途包含確認頁面時的攻擊。
輸入–執行攻擊模式:
- 用戶登錄網站。
- 攻擊者設下圈套。
- 受害人瀏覽惡意網站而出發圈套。
- 攻擊者使用惡意網站中的JavaScript,使受害人的瀏覽器向攻擊對象網站發送將密碼爲攻擊者指定的密碼的POST請求。
- 密碼被更改。
存在確認頁面的CSRF攻擊:
- Hidden參數
- 使用會話變量
根據同源策略,從iframe的外層(惡意網頁)無法讀取到內層(攻擊對象)的內容,因此,CSRF攻擊雖然能夠以正規用戶的權限惡意使用攻擊對象網站中的關鍵處理,卻無法獲取網頁中顯示的內容。
但是,在使用CSRF攻擊成功更改用戶密碼後,攻擊者就知道了更改後的密碼,從而也就能夠登陸應用來竊取被害人的信息了。
修改密碼的三個條件
- 使用POST方法請求
- 保持登錄狀態。
- 使用POST參數中的pwd指定新密碼
CSRF攻擊與XSS攻擊對比:
CSRF是指惡意使用用戶對服務器正常請求中的請求處理,惡意使用的內容僅限於服務器端提供的操作。
而XSS**,在用戶請求服務器中包含的腳本被原封不動的以響應的形式返回,隨後該惡意腳本在用戶的瀏覽器中被執行。由於攻擊者能夠在用戶的瀏覽器上執行自己準備的HTML或JavaScript,因此只要是瀏覽器能做到的事情都可以被用作攻擊手段。**攻擊者甚至還能夠通過JavaScript惡意使用服務器端的功能。
CSRF攻擊安全解決方案
對策分析:
防禦CSRF的關鍵爲確認關鍵處理的請求確實是由正規用戶自願發送的。因此,作爲CSRF的防範策略,需要執行以下兩點:
- 篩選出需要防範CSRF攻擊的頁面
- 使代碼有能力辨認是否是正規用戶的自願請求。
篩選出需要防範CSRF攻擊的頁面:
並非所有的頁面都需要實施CSRF防禦策略**,事實上無需防範CSRF的頁面居多**。通常情況下,Web應用的入口並非只有一處,通過搜索引擎、社交書籤、其他鏈接等方式都能進入Web應用中的各種頁面。比如EC(電子商務)網站一般就非常歡迎通過外部鏈接進入到它的商品展示頁面。而這像這種頁面就不需要實施CSRF對策。
而另一方面,EC網站中的購買商品、更改密碼或確認個人信息等頁面,就不能夠任意由其他網站隨意執行。這樣的頁面就應當實施CSRF防範策略。
確認是正規用戶自願發送的請求:
判斷請求是否爲正規用戶自願發送的實現方法,一般有如下3類:
-
嵌入機密信息(令牌)
如果訪問需要防範CSRF的頁面(登錄頁面、訂單確認頁面等)時需要提供第三方無法得知的機密信息的話,那麼即使出現非正規用戶自願發送的請求,應用端也能夠通過判斷得知請求是否合法。用於此目的的機密信息被稱爲令牌(Token)。會話ID就是一種既簡單又安全的令牌實現方法。
接收令牌的請求(接受關鍵處理的請求)必須爲POST方法。因爲使用GET方法發送機密信息的話,令牌信息就可能通過Referer泄露出去。
-
再次輸入密碼
讓用戶在此輸入密碼,也是用來確認請求是否是由用戶自願發起的一種方法。除了用來防範CSRF攻擊,在此輸入密碼也可以被用於其他目的。
- 在用戶確認下訂單之前,再次向用戶確認購買意向。
- 能夠確認此事在電腦前操作的確實是用戶本人。
要求確認密碼的頁面都應該是在最後的執行頁面。如果僅在途中的某個頁面就行密碼確認,根據代碼實現方法還是可能會存在CSRF漏洞,所以要求輸入密碼的實際非常重要。
-
檢驗Referer
在執行關鍵處理的頁面確認Referer,也是CSRF的一種防範策略。正規請求中Referer的值應該爲執行頁面的上一個頁面(輸入頁面或者確認頁面等)的URL,這一點一定要得到確認。
<?php session_start(); if(preg_match('#\Ahttp://www.baidu.com', @$_SERVER['HTTP_REFERER']) != 1){ die('REFERER檢查失敗'); } if(session_id() !== $_POST['token']){ die('請從正規頁面進行操作'); } ?>
CSRF防範策略比較:
嵌入令牌 | 再次輸入密碼 | 確認Referer | |
---|---|---|---|
開發耗時 | 中 | 中 | 小 |
對用戶的影響 | 無 | 增加了輸入密碼的麻煩 | 關閉了Referer的用戶無法正常使用 |
能否用於手機網站 | 可 | 可 | 不可 |
建議使用的地方 | 最基本的防禦策略,所有情況下均可使用 | 需要防範他人僞裝或者確認需要很強的頁面 | 用戶能夠限定用戶環境的既有應用的CSRF防範策略。 |
CSRF的輔助性對策:
執行完關鍵處理後,建議向用戶註冊的郵箱發送有關鍵處理內容的通知郵件。
發送通知郵件雖然不能防範CSRF攻擊,但是在萬一遭受了CSRF攻擊的情況下能在第一時間讓用戶知情,從而將損害降到最低。
另外,除了CSRF攻擊之外,在攻擊者通過XSS攻擊僞裝成用戶操作關鍵處理時,發送通知郵件也能使用戶儘早發現。
但是,由於郵件是未經加密的明文傳輸,因此,最好不要在郵件中添加重要信息,而只是通知用戶有人惡意執行了關鍵處理如果用戶想要了解詳情的話,可以登錄Web應用查看購買歷史或者發送歷史等內容。
CSRF對策總結:
CSRF漏洞的根本防範策略如下:
- 篩選需要防範CSRF的頁面
- 確認是正規用戶自願發起的請求。
其中,確認是正規用戶自願發起的請求的方法有以下三種。
- 嵌入機密信息(令牌)
- 再次輸入密碼
- 檢驗Referer
另外,作爲CSRF漏洞的輔助性對策,可以執行以下操作。
- 執行完關鍵處理後,向用戶註冊的郵箱發送通知郵件。
自動化掃描程序的檢測方法:
- 在請求和響應過程中檢查是否存在Anti_CSRF token
- 檢查服務器是否驗證Anti-CSRF token
- 檢查token字符串是否可編輯和僞裝
- 檢查Referer頭字段是否可以僞裝
DVWA的CSRF漏洞
安全級別是Low的情況:
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Get input
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Do the passwords match?
if( $pass_new == $pass_conf ) {
// They do!
// Update the database
// Feedback for the user
}
else {
// Issue with passwords matching
}
?>
原理:在安全級別爲low的情況下,CSRF更改密碼時,可以直接進行提交密碼進行改密。沒有安全控制。
攻擊:可以通過在惡意站點上做超鏈接,引誘用戶點擊,從而直接更改密碼。
安全級別是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!
// Update the database
// Feedback for the user
}
else {
// Issue with passwords matching
}
}
else {
// Didn't come from a trusted source
}
?>
原理:在安全級別爲Medium的情況下,主要通過stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false
驗證了Referer字段,從檢查是否來自本網站的更改密碼的請求。
攻擊:可以將惡意站點上惡意鏈接中的html頁面名字改爲包含合法站點域名字符串的文件名,從而讓通過Referer字段的檢查。
安全級別爲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!
// Update the database
// Feedback for the user
}
else {
// Issue with passwords matching
}
// Generate Anti-CSRF token
generateSessionToken();
?>
原理:通過服務器給客戶端生成一個token,在更改密碼時,還需要提交token字段,token可以可以使服務器發給客戶端的一個隨機數。那樣惡意站點就不能知道token從而無法實現繞過用戶直接更改密碼。
攻擊:但是如果合法網站上存在XSS漏洞,那麼可以首先利用XSS漏洞,獲取用戶的Token,然後攜帶用戶的Token去更改密碼。從而達到攻擊的效果。
安全級別爲Impossible的情況:
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$pass_curr = $_GET[ 'password_current' ];
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Sanitise current password input
// Check that the current password is correct
// Do both new passwords match and does the current password match the user?
if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {
// It does!
// Update database with new password
// Feedback for the user
}
else {
// Issue with passwords matching
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
原理:在這情況下,除了驗證token,還需要用戶輸入舊密碼,那麼第三方惡意站點(非法用戶)肯定無法知道舊密碼,再加上token,基本杜絕了CSRF漏洞的攻擊。
學習過程中,筆記的整理與資料的記錄。