?譯者 | 陳峻
審校 | 孫淑娟
跨站點請求偽造(Cross-site request forgery,又稱:跨站點引用偽造)是一種針對Web應(yīng)用的攻擊形式。黑客通過偽裝惡意請求,誘騙用戶運行他們本不打算執(zhí)行的任務(wù)。盡管CSRF可能聽起來與XSS攻擊類似,但它們的執(zhí)行方式存在根本差異。對此,Web服務(wù)器需要一種機(jī)制,來確定瀏覽器所產(chǎn)生的請求,是否源于合法用戶的真實意圖,而非受攻擊的脅迫。針對此類問題,服務(wù)器端可以生成一個唯一的、且不可預(yù)測的密鑰值,作為CSRF令牌被包含在客戶端的HTTP請求中。當(dāng)有后續(xù)請求發(fā)出時,Web服務(wù)器會驗證包含了令牌的請求參數(shù),以拒絕那些不包含有令牌的請求參數(shù)。由于黑客幾乎不可能構(gòu)造一個完整、有效的HTTP請求來欺騙Web用戶,因此該方法通常可被用于防范CSRF攻擊。下面,我將和您討論CSRF令牌的工作原理,及其在應(yīng)用安全中的重要性。
一、為什么需要有效的CSRF令牌?
CSRF令牌通常被建議添加到所有狀態(tài)更改(state-changing)的請求中,以便在后端被執(zhí)行驗證。由于只有應(yīng)用服務(wù)器和客戶端可以識別令牌,因此后端必須確保傳入的請求包含有效的CSRF令牌,以避免XSS或跨站點請求偽造攻擊的得逞。
在基于Cookie的會話期間,作為密鑰值的CSRF令牌需要被安全處理以保持有效。為此,令牌應(yīng)當(dāng)被放置在HTML表單的隱藏字段中,被傳輸?shù)娇蛻舳耍⑹褂肏TTP的POST請求被提交。作為優(yōu)秀的實踐,我們建議使用標(biāo)準(zhǔn)的標(biāo)頭來驗證請求的來源,并使用其他措施去識別和比較來源和目標(biāo)。如果來源匹配,則判定請求是合法的;如果不匹配,則表明疑似跨域請求,應(yīng)予以丟棄。
二、CSRF令牌在防止攻擊中的意義
由于令牌在生成過程中使用到了偽隨機(jī)數(shù)(pseudo-random number)生成器、靜態(tài)密鑰、以及種子時間戳,因此CSRF令牌的值是不可預(yù)測的。同時,每個用戶的令牌也是不同的,而且只會存儲活動的用戶會話。據(jù)此,安全團(tuán)隊可以通過將隨機(jī)數(shù)生成器的輸出,與用戶特定的熵(entropy)連接起來,并對整個結(jié)構(gòu)進(jìn)行散列處理,以提高令牌值的唯一性。對此,黑客將很難在早期的會話Cookie中,根據(jù)已發(fā)布的令牌樣本,去猜測CSRF令牌。
三、如何使用CSRF令牌
盡管我們可以在URL查詢字符串中放置令牌,但是查詢的字符串記錄會被留存在服務(wù)器和客戶端的多條記錄中。因此,查詢字符串可以在客戶端屏幕的瀏覽器上被訪問到,甚至?xí)贖TTP引用標(biāo)頭中,被傳輸給第三方應(yīng)用程序。可見,這種方法是不安全的。對此,我們建議CSRF令牌應(yīng)該被存儲在服務(wù)器端的應(yīng)用程序中,并在自定義請求標(biāo)頭中傳輸CSRF令牌。服務(wù)器端應(yīng)用程序通過按需驗證每個請求,以確保各種有效的請求,包含了與用戶活動會話中存儲的值相匹配的令牌。同時,CSRF令牌也可以對包括POST、PUT和DELETE在內(nèi)的所有HTTP方法執(zhí)行驗證。
四、如何在Java中實現(xiàn)CSRF令牌
由于Java應(yīng)用缺乏了針對CSRF攻擊的固有保護(hù)。因此,我們建議在Java中實現(xiàn)CSRF令牌的時候,請使用一個過濾器和一些輔助類,來啟用令牌的創(chuàng)建、資源的分析、以及響應(yīng)的構(gòu)建。例如,您可以使用通用無狀態(tài)(Generic Stateless)過濾器,來實現(xiàn)了雙重提交(double-submit)的Cookie模式,以啟用CSRF保護(hù)。同時,您可以采用如下工作流程:首先,如下面代碼段所示,過濾器會在Java應(yīng)用的web.xml文件中被定義:
<filter>
<filter-name>CSRFFilter</filter-name>
<filter-class>com.github.adriancitu.csrf.GenericCSRFStatelessFilter</filter-class>
<filter>
<filter-mapping>
<filter-name>CSRFFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
該過濾器包括了兩個可選的初始化變量:
1.csrfHeadername– 包含了令牌的標(biāo)頭名稱
2.csrfCookieName– 用于存儲CSRF令牌的Cookie的ID。
對于每個HTTP請求,過濾器都會提供一個ExecutionContext類的實例。它包含了CSRFcookie、以及HTTP請求與響應(yīng)的Java對象。同時,該類也包含了ResourceCheckerHook、ResponseBuilderHook和TokenBuilderHook等輔助類的實現(xiàn)。
接著,過濾器會根據(jù)如下三種情況,去檢查所請求的HTTP資源的保護(hù)狀態(tài):
1.MUST_NOT_BE_PROTECTED
2.MUST_BE_PROTECTED_BUT_NO_COOKIE_ATTACHED
3.MUST_BE_PROTECTED_AND_COOKIE_ATTACHED
對于狀態(tài)為MUST_NOT_BE_PROTECTED和MUST_BE_PROTECTED_BUT_NO_COOKIE_ATTACHED的資源,過濾器會生成一個由TokenBuilderHook類提供的CSRF令牌的Cookie。對于帶有標(biāo)簽MUST_BE_PROTECTED_AND_COOKIE_ATTACHED的資源,過濾器則使用ResourceCheckerHook去檢查資源的CSRF保護(hù)狀態(tài),然后使用類ResponseBuilderHook向客戶端返回一個響應(yīng)。當(dāng)然,上述代碼只是一個參考示例,在實際運用中,還需要開發(fā)團(tuán)隊在源代碼中進(jìn)一步構(gòu)建CSRF緩解機(jī)制。
五、如何在PHP中實現(xiàn)CSRF令牌
由于PHP使得開發(fā)人員能夠創(chuàng)建具有交互功能的動態(tài)網(wǎng)站,因此它在內(nèi)容管理系統(tǒng)領(lǐng)域備受歡迎。不過,我們需要在PHP聯(lián)系人和用戶輸入的表單中,通過實現(xiàn)post處理程序,對傳入請求予以驗證,讓網(wǎng)站免受CSRF的攻擊。在PHP聯(lián)系人表單中,實現(xiàn)CSRF保護(hù)的典型工作流程如下:首先,我們需要在登錄頁面上創(chuàng)建一個表單的footer腳本。該腳本會調(diào)用SecurityService(一個PHP類),生成令牌,并啟動PHP會話。SecurityService會被寫入這個用于驗證請求的令牌,并將令牌加載到隱藏字段中。如下代碼展示了典型的SecurityService.php配置:
public function getCSRFToken()
{
if (empty($this->session[$this->sessionTokenLabel])) {
$this->session[$this->sessionTokenLabel] = bin2hex(openssl_random_pseudo_bytes(32));
}
if ($this->hmac_ip !== false) {
$token = $this->hMacWithIp($this->session[$this->sessionTokenLabel]);
} else {
$token = $this->session[$this->sessionTokenLabel];
}
return $token;
}
左右滑動查看完整代碼然后,頁面上呈現(xiàn)PHP聯(lián)系人表單,以便用戶輸入諸如主題、消息、姓名和電子郵件等詳細(xì)信息。表單還應(yīng)當(dāng)在隱藏字段中包含已生成的標(biāo)記--csrf-token。在用戶單擊提交按鈕后,應(yīng)用程序?qū)?zhí)行JQuery表單驗證,并將各種參數(shù)發(fā)布到PHP上。
此外,在聯(lián)系人表單被提交后,該表單會執(zhí)行一個腳本,將已嵌入的令牌與會話中存儲的令牌進(jìn)行比較。如果令牌匹配,應(yīng)用程序會去響應(yīng)用戶的請求。如果不匹配,PHP將通過錯誤消息去告知用戶。
六、如何在Django中實現(xiàn)CSRF令牌
Django提供了一個開箱即用的CSRF中間件標(biāo)簽,您可以輕松啟用它,以抵御CSRF攻擊。如下工作流將向您展示如何在已有的框架內(nèi),實現(xiàn)針對CSRF的防護(hù):在Django中,CSRF中間件是被默認(rèn)啟用的。如果開發(fā)人員想覆蓋此設(shè)置,則應(yīng)該在任何視圖之前事先聲明django.middleware.csrf.CsrfViewMiddleware,以啟用CSRF令牌驗證。對于一些特定視圖,開發(fā)人員可以調(diào)用csrf-protect裝飾器(decorator)。此類裝飾器可用于在輸出中插入CSRF令牌的視圖。如下代碼段展示了裝飾器的基本配置:
from django.shortcuts import render
from django.views.decorators.csrf import csrf_protect
@csrf_protect
def my_view(request):
c = {}
# ...
return render(request, "a_template.html", c)
左右滑動查看完整代碼開發(fā)人員可以通過在<form>元素中包含csrf_token標(biāo)簽,來啟用對使用POST表單的模板的保護(hù)。當(dāng)然,這僅適用于那些具有內(nèi)部URL的表單,并不適用于針對外部URL的POST表單。對于外部URL,我們可以使用如下方法,來調(diào)用CSRF令牌的標(biāo)記:
<form method="post">{% csrf_token %}
此外,我們還建議使用RequestContext,在各種相應(yīng)的Django視圖中呈現(xiàn)響應(yīng)。
七、如何在Javascript中實現(xiàn)CSRF令牌
所有Javascript框架都帶有實現(xiàn)CSRF防護(hù)的默認(rèn)選項。例如,Express.js就包含了csurf中間件,可協(xié)助創(chuàng)建和驗證各種令牌。請使用如下流程來啟用csurf,并達(dá)到CSRF的防護(hù)效果:首先,請在index.js文件中包含以下代碼:
app.get('/', csrfProtection, (req, res) => {
res.render('index', { csrfToken: req.csrfToken() });
左右滑動查看完整代碼接著,請使用類似如下代碼的配置,將index.ejs添加到各個視圖文件夾中:
<input type='hidden' name='_csrf' value='<%= csrfToken %>'>
<label for='name'> Name:</label>
<input type='text' name='name'>
<button type='submit'> Update </button>
</form>
左右滑動查看完整代碼主配置文件中的“/”路由會在index.ejs模板中呈現(xiàn)csrfToken變量,并在隱藏字段中插入令牌。當(dāng)用戶提交表單時,對應(yīng)的請求會被添加到“/profile” 路由中,以供CSRF令牌驗證。如果缺少此CSRF令牌,應(yīng)用程序?qū)⒎祷匾粋€無效的CSRF令牌錯誤。
八、如何修復(fù)無效的CSRF令牌
正如前文所述,CSRF攻擊將導(dǎo)致用戶會話被未經(jīng)驗證的訪問,并產(chǎn)生嚴(yán)重的后果。為了防范此類攻擊,確保用戶使用有效令牌來發(fā)布請求,我們需要通過如下方法來及時修復(fù)和防止無效令牌:
- 使用自定義的請求標(biāo)頭
在易受攻擊的應(yīng)用程序中,添加CSRF令牌往往需要更改用戶界面等復(fù)雜的管理任務(wù)。而作為替代方案,安全團(tuán)隊可以構(gòu)建自定義的請求標(biāo)頭,以使用同源策略(same-origin policy)來加強(qiáng)CSRF防御。安全策略通過強(qiáng)制限制自定義的標(biāo)頭只能在Javascript中被構(gòu)建,并且只能在其源頭中被使用的方式,來拒絕各種跨域請求。
- 利用內(nèi)置和現(xiàn)有的CSRF處置措施
在大多數(shù)情況下,許多開發(fā)框架都包含了,內(nèi)置于安全套件中的同步器令牌的防御機(jī)制。它們可以有效地保護(hù)整個應(yīng)用程序的技術(shù)棧。如果在開發(fā)團(tuán)隊現(xiàn)有的技術(shù)棧中,所用到的框架已經(jīng)內(nèi)置提供了默認(rèn)的抵御CSRF的選項,那么您完全可以嘗試著在此基礎(chǔ)上,根據(jù)業(yè)務(wù)用例的實際,進(jìn)行自定義和配置實施。
- 部署基于UI的CSRF防御
CAPTCHA、重新驗證(re-authentication)的認(rèn)證機(jī)制、以及一次性令牌之類的機(jī)制,都可以通過分析請求,來防范那些通過CSRF進(jìn)行未經(jīng)授權(quán)的操作,并過濾掉各種非法操作請求。它們雖然提供了強(qiáng)大的驗證與防御措施,但是我們需要通過部署UI來改變用戶的體驗,以便更好地處理關(guān)鍵性的安全操作。
原文鏈接:https://dzone.com/articles/what-is-a-csrf-token
譯者介紹
陳峻 (Julian Chen),51CTO社區(qū)編輯,具有十多年的IT項目實施經(jīng)驗,善于對內(nèi)外部資源與風(fēng)險實施管控,專注傳播網(wǎng)絡(luò)與信息安全知識與經(jīng)驗;持續(xù)以博文、專題和譯文等形式,分享前沿技術(shù)與新知;經(jīng)常以線上、線下等方式,開展信息安全類培訓(xùn)與授課。?