
每個 Java 軟件架構師都應該知道的 20 件事
How much do you want to send?
<input name="amount" />
To whom do you want to send it?
<input name="receiver" />
<input type="submit" value="Send" />
</form>
攻擊者在 BankApp 網站上以用戶名 @evil 開設賬戶。攻擊者撰寫了兩封單獨的釣魚郵件。第一封郵件假裝來自 BankApp,內容是“警告!您的 BankApp 賬戶中檢測到潛在欺詐行為。”第二封郵件內容是“您可能贏了 1,000 美元。”第一封郵件的目的是鼓勵用戶登錄 BankApp。第二封郵件包含以下標記:HTML
<form method="post" action="https://bankapp.com/transfer">
<input name="amount" type="hidden" value="1000" />
<input name="recipient" type="hidden" value="@evil" />
</form>
<script>document.forms[0].submit();</script>
用戶甚至無需點擊任何內容。第二封電子郵件提交表單并在用戶瀏覽器中呈現后立即執行攻擊。攻擊者使用這兩封電子郵件向 100,000 名用戶發送垃圾郵件。其中一些用戶可能擁有 BankApp 帳戶。其中一些用戶會打開電子郵件,攻擊者從每封電子郵件中獲得 1000 美元。
服務器通過 HTTP 公開的任何狀態更改都可能受到偽造請求的影響。CSRF 攻擊可以更改密碼、發布社交媒體帖子、刪除帳戶、配置路由器以及許多其他危險或破壞性的操作。
BankApp 示例假設您必須通過 POST 表單來轉賬。并非所有 Web 端點都以這種方式編寫。如果 BankApp 網站允許使用 GET 請求轉賬,那么攻擊者的電子郵件可能只會這樣做:HTML
<IMG src="http://bankapp.com/transfer?amount=1000&recipient=@evil">
當用戶在釣魚郵件中看到損壞的圖像標簽時,1000 美元就已經沒了。
其他可改變狀態的 HTTP 動詞也可能被利用。只有 PATCH、POST、PUT 和 DELETE應允許更改狀態,但如果 GET、HEAD 和 OPTIONS 也能進行更改,則可能存在漏洞。
不要忽略登錄頁面上的 CSRF 警告。在登錄時,即使用戶尚未通過身份驗證,CSRF 攻擊仍然可能是一種威脅,盡管方法和風險與經過身份驗證的 CSRF 不同。在登錄 CSRF 攻擊中,攻擊者偽造請求,使用攻擊者的登錄憑據將用戶登錄到站點。受害者可能會認為自己已經登錄了自己的帳戶,從而留下攻擊者可以使用的信息。針對 Google 和 Yahoo 的登錄 CSRF 攻擊可能會將受害者的搜索歷史記錄暴露給攻擊者。針對 PayPal 等網站的登錄攻擊可能會誘使用戶提供信用卡信息,然后攻擊者可以登錄并使用這些信息。
CSRF 攻擊依賴于能夠預測瀏覽器請求的內容,從而偽造瀏覽器請求。針對 CSRF 的最強大防御方法是使每個請求的內容不可預測。基于令牌的防御依賴于隨機生成的值來做到這一點。
為了保護自己,服務器會向每個表單添加一個隱藏字段,為每個會話(或每個請求)分配一個不同的隨機值。服務器會確認每個后續請求都具有此字段,并且其值與為會話分配的值相匹配。這稱為同步器令牌模式。
同步器令牌要求服務器在服務器端存儲隨機會話值。如果您不想在服務器上保留狀態,則可以使用只有服務器上才知道的私鑰加密令牌值。加密值應該是時間戳。通過確認每個請求都包含隱藏字段、您可以解密字段值、解密值是時間戳以及時間戳是最近的(令牌未過期)來驗證后續請求。
加密令牌是一種無狀態的 CSRF 防御措施,而雙重提交的 cookie 是另一種。在這種防御措施中,隨機令牌值保存在客戶端的 cookie 中。每個后續請求都必須在隱藏字段中包含相同的值。在每次請求中,服務器都會確認隱藏字段是否存在,并且其值與 cookie 中的值匹配。
為了確保這種防御的可靠性,您必須使用 HTTPS(無論如何都推薦使用)并確保您的子域也是安全的。
AJAX 調用發生在基于 Cookie 的瀏覽器會話上下文中,也容易受到 CSRF 攻擊。AJAX 調用通常不會發布可以放置隱藏字段的表單。相反,它會在自定義 AJAX 標頭中將 CSRF 令牌返回給服務器。JavaScript
var xhr = new XMLHttpRequest();
xhr.setRequestHeader(tokenname, tokenvalue);
將tokenname
和替換tokenvalue
為生成 AJAX 請求的頁面中隱藏的隨機令牌字段的名稱和值。
使用自定義 HTTP 標頭是一種防御措施,因為只有 JavaScript 可以創建自定義標頭,并且瀏覽器的單源策略 (SOP) 會阻止跨站點 JavaScript 調用。
許多編程框架(例如Spring、Django、.NET和AngularJS )都帶有基于令牌的 CSRF 防御的內置實現,這些實現可以生成加密性強的隨機令牌值并在每次請求時對其進行驗證。其他一些框架也有可用的附加組件:例如,適用于 Java 的OWASP CSRFGuard和適用于 PHP 的CSRFProtector 項目。正確實施基于令牌的防御可能很棘手,因此如果有人已經為您制定了可靠的實現,請不要自行構建。
基于令牌的防御效果最好,但縱深防御方法需要考慮額外措施來阻止可能的 CSRF 攻擊。
SameSite是一種 Cookie 屬性,類似于Secure、HTTPOnly和Expires。SameSite 屬性讓網站設計者決定瀏覽器何時應在跨站點請求中包含 Cookie。SameSite 的值可能是Strict或Lax,任何一個值都會阻止至少一些 CSRF 請求。這是一種有用的防御措施,但僅靠它還不夠,因為并非所有瀏覽器都支持它(大多數瀏覽器都支持),而且它可以被繞過。
要確認請求是否有效,請檢查 HTTP 標頭以確認來源和目標值是否匹配。要確定請求的來源,請檢查 Origin 或 Referer 標頭。將其與請求的目的地進行比較:目標來源。這些值無法偽造。只有瀏覽器才允許設置它們。
這種防御措施的優點是,甚至在身份驗證之前就可以識別跨站點請求。但是,一小部分網絡流量可能會出于正當理由忽略請求來源,并且如果您的應用程序位于代理后面,則確定目標來源并非易事。
有時(尤其是對于高風險交易)直接讓用戶參與是緩解 CSRF 和其他偽造請求的有效方法。要求用戶提供密碼或一次性令牌,或者通過 CAPTCHA,可以提供有效的保護。
這些提示將幫助您避免防御 CSRF 時常見的問題。
如果未正確實施,基于令牌的防御措施可能會被攻破。為了確保隨機令牌值無法預測,應使用加密性強的隨機數生成器來創建令牌值。此外,請謹慎使用將生成的值與請求中收到的值進行匹配的邏輯。驗證邏輯中的錯誤導致GlassDoor 遭受 CSRF 攻擊。在 GlassDoor 實施的某些情況下,檢查失敗會引發異常,但該異常僅被記錄下來,代碼會繼續執行,就像驗證成功一樣。
此外,跨站點腳本漏洞 (XSS)可以擊敗任何 CSRF 保護。如果攻擊者可以從用戶的瀏覽器中檢索令牌,即使是完美實施的基于令牌的 CSRF 防御也幾乎無法提供保護。可靠的 XSS 防御是 CSRF 防御的先決條件。
這些措施有時被視為緩解措施,但卻不起作用:
多步驟交易
如果攻擊者仍然可以預測事務的每個步驟,那么將改變服務器狀態的操作分成多個 HTTP 請求是沒有幫助的。
秘密cookie
創建攻擊者無法預測的秘密 cookie 毫無用處,因為瀏覽器總是在每次請求時發回所有相關 cookie。攻擊者無需猜測秘密 cookie。
HTTPS
單純加密網絡流量并不能阻止 CSRF 攻擊。然而,對于其他一些緩解措施(例如雙重提交的 cookie)而言,這是必要的步驟,并且在所有情況下都可取,以使其他措施更加可靠。
URL 重寫
您可以在頁面加載時修改 URL,從而消除會話 cookie,但這樣做會在 URL 上暴露會話 ID,從而增加攻擊者捕獲會話 ID 的風險。
跨站點腳本 (XSS) 漏洞與跨站點請求偽造 (CSRF) 漏洞具有一些共同的特征。兩者都旨在在受害者的合法網絡會話環境中運行惡意代碼。然而,XSS 旨在將惡意代碼直接注入易受攻擊的頁面,而 CSRF 通常依賴于社交工程(例如網絡釣魚電子郵件)將惡意代碼放入受害者瀏覽器中的不相關頁面。XSS 依賴于目標網站中的缺陷,這些缺陷允許注入惡意代碼,服務器將把這些惡意代碼作為其自身網頁的一部分傳遞給受害者。
差異很大。惡意 XSS 腳本可以直接訪問目標站點中的頁面。事實上,XSS 漏洞可以向攻擊者暴露頁面上的所有內容,包括任何反 CSRF 令牌值。即使是完美的防御措施也幾乎無法為易受 XSS 攻擊的站點提供 CSRF 保護。
漏洞測試是任何安全程序的基本組成部分。掃描工具能夠發現許多 CSRF(和 XSS)漏洞。鑒于此類漏洞的潛在嚴重性,任何擁有 Web 應用程序的人都應系統地測試它們 – 最好將自動掃描作為 CI/CD 管道的一部分。漏洞測試需要遵守最佳實踐和標準,例如 NIST SP 800-53 和 ISO 27001。高風險應用程序還應在其安全程序中包括手動滲透測試。
文章來源:What is Cross-Site Request Forgery (CSRF)?