- 原文連接:Cross-Site Request Forgery is dead!
- 原文做者:Scott
- 譯文出自:掘金翻譯計劃
- 譯者:XatMassacrE
- 校對者:newbieYoung,DeadLion
在接二連三的被跨站請求僞造折磨了這麼多年後,咱們如今終於有了一個合理的解決方案。一個對網站擁有者沒有技術負擔、實施起來沒有難度、部署又很是簡單的方案,它就是 Same-Site Cookies。javascript
跨站請求僞造(又被稱爲 CSRF 或者 XSRF )彷佛一直都存在着。它源自一個網站必須向另外一個網站發出請求的簡單功能。好比像在頁面中嵌入下面的表單代碼。html
<form action="https://your-bank.com/transfer" method="POST" id="stealMoney">
<input type="hidden" name="to" value="Scott Helme">
<input type="hidden" name="account" value="14278935">
<input type="hidden" name="amount" value="£1,000">複製代碼
當你的瀏覽器載入這個頁面以後,上面的表單將會由一個簡單的 JS 片斷來實現提交。java
document.getElementById("stealMoney").submit();複製代碼
這就是被稱做 CSRF 的來歷。我僞造了一個跨站到你的銀行網站的請求。這個問題的關鍵不是我發送了請求,而是你的瀏覽器經過這個請求發送了你的 cookies。此時,你當前擁有的所有驗證信息也會經過這個請求發送,這就意味着你登陸你的銀行帳戶而且捐助了我 £1,000 。謝謝啊!那麼當你沒有登陸的時候,這個請求對你就沒有什麼影響了,由於你不登陸是沒法轉帳的。不過對於銀行來講,他們如今採用的下面幾種辦法能夠在必定程度上防護 CSRF 攻擊。git
關於緩解 CSRF 這裏就不詳細展開講了,由於網上關於這個話題已經有大量的信息了,可是我仍然會快速的過一遍順便展現一下實現他們都須要哪些技術。github
當咱們收到一個請求時,關於這個請求的來源有兩個地方的信息對咱們來講是有用的。一個是 Origin header,另外一個是 Referer header。你能夠檢查他們中的一個或者兩個的值來斷定對於你的網站來講他們是否是來自一個不一樣的域。若是這個請求是跨域的,那麼你把它丟掉就能夠了。Origin 和 Referer header 會在瀏覽器端作一些保護措施來阻止被纂改,可是這並不老是有效的。web
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 accept-encoding: gzip, deflate, br cache-control: max-age=0 content-length: 166 content-type: application/x-www-form-urlencoded dnt: 1 origin: https://report-uri.io referer: https://report-uri.io/login upgrade-insecure-requests: 1 user-agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36複製代碼
一般狀況下你能夠經過兩種方法來實現 Anti-CSRF tokens,可是它們的原理是同樣的。當一個遊客請求一個頁面時,相似於上面提到的轉帳頁面,你能夠在表單中嵌入一個隨機的token。當真正的用戶提交表單的時,你就會收到表單的隨機 token,這樣你就能夠經過以前嵌入的那個隨機 token 來校驗了。在 CSRF 攻擊場景中,攻擊者永遠都不可能拿到這個值甚至在攻擊者能夠請求到頁面的狀況也沒法拿到,由於同源策略(SOP)會阻止攻擊者從包含 token 的響應中讀取內容。這個方法在實際運用中很不錯,可是它須要網站追蹤每個請求而且返回 Anti-CSRF tokens。還有一個相似的在表單中嵌入 token 的方法是給瀏覽器一個包含相同值的 cookie 來實現的。當網站收到真正的用戶提交他們的表單時,cookie 中的值和表單中的值將會相匹配。攻擊者經過沒有 CSRF cookie 的瀏覽器發送僞造的請求將會失敗。跨域
<form action="https://report-uri.io/login/auth" method="POST">
<input type="hidden" name="csrf_token" value="d82c90fc4a14b01224gde6ddebc23bf0">
<input type="email" id="email" name="email">
<input type="password" id="password" name="password">
<button type="submit" class="btn btn-primary">Login</button>
</form>複製代碼
在很長一段時間,上面的這些方法在面對 CSRF 時給咱們提供了強勁的保護。檢查 Origin 和 Referer header 並非 100% 有效的,大部分網站也會經過一些高級的 Anti-CSRF token 方式來防護。問題是,這兩種方法都須要網站有一些必要的條件才能實施和維護。雖然這些條件並非世界上最複雜的技術,可是咱們仍然須要創建一個解決辦法來讓瀏覽器作一些咱們不想讓它作的事情。既然這樣的話,那麼咱們爲何不直接告訴瀏覽器不要作那些咱們不想讓它們作的事情呢?如今,咱們能夠了!瀏覽器
你或許已經在我最近的博客( Tough Cookies)上看到了一些關於 Same-Site Cookies 的內容,可是在這裏我將會用一些例子來深刻的講解。從本質上來說,Same-Site Cookies 能夠徹底有效的阻止 CSRF 攻擊,是的,CSRF 一點機會都沒有。咱們在互聯網上真正需求的本質就是贏得網絡安全的戰爭,Same-Site Cookies 很是容易部署,是真的很是容易。找到你原來的 cookie :安全
Set-Cookie: sess=abc123; path=/複製代碼
添加 SameSite 這個屬性。cookie
Set-Cookie: sess=abc123; path=/; SameSite複製代碼
你已經完成了。嚴格來說,就是這樣!在 cookie 上啓用這個屬性將會告訴瀏覽器給予這個 cookie 確切的保護。你能夠經過 Strict 和 Lax 這兩種模式來啓用這個保護,具體用哪一種模式取決於你想要的嚴格程度。若是在你的 cookie 設置中沒有指定模式的話默認將會使用 Strict 模式,可是若是你想的話你能夠明確的指定是 Strict 仍是 Lax。
SameSite=Strict
SameSite=Lax複製代碼
很顯然,將你的 SameSite 保護設置爲 Strcit 模式是一個更好的選擇,可是咱們之因此有兩個選項的緣由是由於不是全部的網站都是同樣的而且不是全部的網站都有一樣的需求。當咱們在 Strict 模式下操做時,瀏覽器在任何跨域請求中都不會攜帶 cookie,因此說 CSRF 一點機會都沒有。可是問題是,頂級導航(直接在地址欄改變 URL )的請求都不會攜帶 cookie。好比說有一個連接地址 facebook.com 而且 Facebook 的 SameSite cookies 的模式爲 Strict,當你點擊連接打開 Facebook 以後你會發現你沒法登陸。不管你以前是否登陸,在新標籤中打開,不管你怎麼作,當你從那個連接過來時你都沒法登陸到 Facebook。這就很煩人了,而且咱們的用戶也不但願咱們提供如此蛋疼的保護。這時候 Facebook 要作的就是向 Amazon 學習,使用兩個 cookie。一種是用來驗證用戶信息和登陸操做的 '基礎的' cookie,當你想進行一些相似於支付,改變帳戶信息的敏感操做時就須要第二種 cookie 了,'真正的' cookie 就能夠容許你進行一些重要的操做。在這個案例中第一種 cookie 就是一種 '方便的' 不會設置 SameSite 的 cookie,它真的不回容許你進行任何敏感性的操做,即便攻擊者經過它來進行跨站請求,什麼都不會發生。第二種 cookie 是一種設置了 SameSite 屬性的 '敏感的' cookie,攻擊者在跨站請求中不會獲取它的權限。這對於用戶和安全來講就是一種理想的解決方案。然和這種方式的實施性並不強,由於咱們但願 SameSite cookies 能夠簡單的部署,那麼咱們就須要第二個選項了。
將 SameSite 保護設置爲 Lax 模式將會解決上面提到的在 Strict 模式下的用戶在已經登陸的前提下點擊連接仍然沒法在目標網站登陸的問題。在 Lax 模式下有一個例外,就是在頂級導航中使用一個安全的 HTTP 方法發送的請求能夠攜帶 cookie。所謂 "安全的" 的 HTTP 方法在 Section 4.2.1 of RFC 7321 定義爲 GET、HEAD、OPTIONS 和 TRACE,在這裏咱們只關心 GET 方法,就是咱們連接到 facebook.com 的頂級導航就是一個 GET 方法。如今當用戶點擊一個設置了 SameSite 的連接以後,瀏覽器就會發送攜帶 cookie 和一些咱們但願的用戶信息的請求。同時,咱們也防範了基於 POST 方法的 CSRF 攻擊。在 Lax 模式下,最開始提到的例子中的攻擊手段也沒法成功。
<form action="https://your-bank.com/transfer" method="POST" id="stealMoney">
<input type="hidden" name="to" value="Scott Helme">
<input type="hidden" name="account" value="14278935">
<input type="hidden" name="amount" value="£1,000">複製代碼
由於 POST 方法被認爲是一種不安全的方法,瀏覽器在請求中是不會攜帶 cookie 的。那麼攻擊者固然會想到使用一種 '安全的' 方法來完成一樣的請求。
<form action="https://your-bank.com/transfer" method="GET" id="stealMoney">
<input type="hidden" name="to" value="Scott Helme">
<input type="hidden" name="account" value="14278935">
<input type="hidden" name="amount" value="£1,000">複製代碼
其實只要咱們在接收 POST 請求的地方不接受 GET 請求那麼這種攻擊方法就會失效,可是在 Lax 模式下還有一些須要注意的點。好比,若是一個攻擊者觸發一個頂級導航或者彈出一個新的窗口,那麼他們就可讓瀏覽器發送一個攜帶 cookies 的 GET 請求。這就是在 Lax 模式下須要取捨的地方,咱們在保證完整的用戶體驗的前提下不得不承擔一些小的風險。
這篇博客的目標是經過 SameSite Cookies 來緩解 CSRF 攻擊,可是,你可能已經猜到了,這種機制還有一些其餘的用途。第一個就是跨站腳本包含(XSSI),它是指當瀏覽器向相似於腳本的資源文件發送請求的時候將會根據用戶是否登陸而作出改變。在跨站請求的場景中,一個攻擊者沒法使用 SameSite Cookie 的一些驗證信息來形成不一樣的響應。這裏還有一些有趣的定時攻擊的詳細信息。
還有一個有趣的用途(不是很詳細)是用來對抗在面對渾水猛獸般的攻擊手段下 (CRIME), BREACH), HEIST, TIME) 形成的會話 cookie 的泄露。這些確實是很高級的攻擊手段,可是基礎的場景是一個 MiTM (中間人攻擊) 能夠經過任何他們喜歡或監視的機制來強行讓瀏覽器發送跨域請求。經過使用請求載荷的大小的變化,攻擊者能夠變動瀏覽器請求並觀察每次變動以後的大小就能夠猜出一位 session ID 的值。而使用 SameSite Cookies 的話,瀏覽器在發送這些請求的時候就不會攜帶 cookies,那麼攻擊者業就沒法猜到他們的值了。
和不少新的瀏覽器安全特性同樣,咱們老是但願 Firefox 和 Chrome 可以引領這些新特性,可是此次狀況不同了。Chrome 自從 v51 就開始支持 SameSite Cookie 了,這意味着 Opera,安卓瀏覽器和安卓上的 Chrome 都支持這一特性。你能夠在 caniuse.com 上看到當前全部支持該屬性的詳細信息,Firefox 還有一個開放的 bug 須要添加支持。雖然目前來看支持並非很全面,可是咱們應該給咱們的 cookies 添加 SameSite 這個屬性。支持這一特性的瀏覽器將會按照協議爲咱們的 cookie 提供額外的保護,而不支持的瀏覽器會直接無視它。這不但對咱們沒什麼影響,還會提供一種不錯的具備深度的防護手段。雖然離咱們徹底移除傳統的反 CSRF 機制還有很長的一段時間,可是添加 SameSite 仍然能夠爲咱們提供一個足夠健壯的保護。