從跨域與同源策略談CSRF防御與繞過


之前偶然看到群里有小伙汁問這個token相關的問題,當時我醞釀了一下子,沒想好怎么總結,今天來說一下

CSRF在過去還屬於OWASP TOP10 ,現在已經不是了(補充一點:關於OWASP API 請參考https://www.cnblogs.com/iamver/p/14331357.html)

 

 

 

在實際中也沒咋遇到過(不排除自己太菜的原因),但是一些原理還是有必要了解一下的 ,比如網上最常說的防御與繞過

 

 

 

0x01 前言

 

CSRF-Cross Site Request Forgery 跨站請求偽造

之前我在DVWA那部分介紹過一些CSRF(感興趣的可以往前翻翻往期隨筆)

 

原理簡單來說,就是這張圖

 1. 用戶C打開瀏覽器,訪問受信任網站A,輸入用戶名和密碼請求登錄網站A;

​ 2. 在用戶信息通過驗證后,網站A產生Cookie信息並返回給瀏覽器,此時用戶登錄網站A成功,可以正常發送請求到網站A;

​ 3. 用戶未退出網站A之前,在同一瀏覽器中,打開一個TAB頁訪問網站B;

​ 4. 網站B接收到用戶請求后,返回一些攻擊性代碼,並發出一個請求要求訪問第三方站點A;

 5. 瀏覽器在接收到這些攻擊性代碼后,根據網站B的請求,在用戶不知情的情況下攜帶Cookie信息,向網站A發出請求。網站A並不知道該請求其實是由B發起的,所以會根據用戶C的Cookie信息以C的權限處理該請求,導致來自網站B的惡意代碼被執行

 

額,所以說想利用CSRF的條件是:受害者正在訪問你要攻擊的網站A,且同時又打開了你的惡意鏈接

釣魚佬狂喜

 實際上,CSRF更像是一種“形式”,側重於討論對於用戶請求的“偽造”、“篡改”,因此上述關於漏洞過程的描述中沒有提到具體的、固定的代碼和操作,而是簡單描述了一下整個流程

 

 

 

 

突然想起一個事,覺得十分有必要說一下

所以這里補充一個知識點,關於跨域的問題

 

眾所周知,瀏覽器存在同源策略(SOP即same-orgin policy),這個策略限制了不同源之間如何進行數據交互,屬於一種安全機制

是否同源由URL的協議、域名、端口號決定,如果兩個URL 的這三者均相同,則認為他們是同源的,否則有一個不同就是不同源,不同源之間相互訪問資源會受到某些限制(注意不是任何資源都完全訪問不了,而是某些方法會受到限制比如Document對象的很多屬性啦、XHR生成的HTTP請求啦。。。)

 

那可麻煩了,如果按照這個標准,不同源的多了去了,難道都不能訪問了?

於是就有了跨域,實現繞過同源策略

關於同源策略

  • 通常允許跨域寫操作(link、redirect、表單提交)
  • 通常允許跨域資源嵌入(script、img、video...)
  • 通常禁止跨域讀操作(ajax)
  • 可以正常發送請求,可以攜帶Cookie(withCredentials),但是瀏覽器會限制來自於不同域的資源的接收

請看下圖:

 

 即:

1.無法讀取非同源網頁的 Cookie、LocalStorage 和 IndexedDB

2.無法接觸非同源網頁的 DOM

3.向非同源地址發送 AJAX 請求瀏覽器會拒絕響應

 

(注:AJAX(Asynchronous JavaScript + XML)是一種描述現有技術集合及標准的模型,主要是網頁增量更新用的,不用重新加載整個界面。雖然說是叫XML,但是常用json。AJAX常見的格式如下,更詳細的內容參考:https://www.cnblogs.com/caifenglin/p/7797811.html,我就不細說了)

<script type="text/javascript">
    window.onload = function () {//頁面刷新即加載
    $.ajax({
        url:'http://www.lcx.com',  //請求URL地址
        type:'GET', //請求方式GET POST
        async:true,    //是否異步,或寫false即同步,默認為true即異步
        timeout:5000,    //超時時間
        dataType:'json',    //返回的數據格式:json/xml/html/script/jsonp/text

        beforeSend:function(xhr){//請求發送之前進行的操作
            console.log(xhr)
            console.log('發送前')
        },
        success:function(data,textStatus,jqXHR){//請求成功之后的返回結果
            console.log(data);
        },                  //還可以加上complete:function(...){......},做請求完成的處理
        error:function(xhr,textStatus){//請求失敗之后的返回錯誤信息
            console.log('錯誤',xhr.responseText);
            console.log(xhr);
            console.log(textStatus);
        }
    })


}
</script>

 

常見的跨域有JSONP跨域與CORS跨域(當然不止這兩種方法,還有很多種,例如:https://zhuanlan.zhihu.com/p/81809258,這篇文章也只是列舉了一些作者認為主流的方式,還有其他的沒列舉出來)

 

 JSONP(json with padding)利用了<script>標簽的跨域能力實現了跨域數據訪問(就比如說script標簽的src不受同源策略限制),請求動態生成的JavaScript腳本同時帶一個callback函數名作為參數。服務端收到請求后,動態生成腳本產生數據,並在代碼中以產生的數據為參數調用callback函數

 詳情我就不再展開了,請參考:https://cloud.tencent.com/developer/article/1058337?from=information.detail.jsonp%E8%B7%A8%E5%9F%9F%E5%8E%9F%E7%90%86 (話說我今天要講啥來着???)

抓包的時候,如果看到返回數據包中有長類似這樣callback({ "name": "lcx" , "msg": "獲取成功" }); 的東西,那就是JSONP格式的

關於JSONP格式與JSON的區別請看:https://blog.csdn.net/SmCai/article/details/86647288

JSONP威脅點通常有兩個:1、對於輸入的callback函數名過濾不嚴格,導致輸入的數據直接輸出到前端造成xss  2、JSONP劫持漏洞,由於對於來源域沒有嚴格限制,因此來源於不安全的域的請求也會被響應

關於JSONP劫持漏洞參考:https://saucer-man.com/information_security/309.html、https://xz.aliyun.com/t/5143

 

CORS是一個W3C標准,全稱是"跨域資源共享"(Cross-origin resource sharing)。通過該標准,可以允許瀏覽器向跨源服務器發出XMLHttpRequest 請求,從而克服了AJAX只能同源使用的限制,進而讀取跨域的資源。CORS允許Web服務器通知Web瀏覽器應該允許哪些其他來源從該Web服務器的回復中訪問內容

簡單來說,采用了簡單請求的請求頭中附帶origin或者非簡單請求用預檢機制,並根據服務器返回的響應內容來實現跨域的約定

 詳情我也不展開了(展開能說到地老天荒),請參考:https://www.cnblogs.com/zhaosq/p/10511875.html、https://www.cnblogs.com/zhaosq/p/10529803.html

 CORS配置漏洞參考:https://zhuanlan.zhihu.com/p/83099266、https://www.freebuf.com/articles/web/204023.html

 

關於這兩種方法的跨域,這篇文章總結得很好:https://www.freebuf.com/articles/web/208672.html

JSONP與CORS漏洞總結:https://www.freebuf.com/articles/web/207802.html

 

說了一下跨域的內容,實際上有些通過這種跨域帶來的安全問題引發的攻擊也屬於廣義上的CSRF

比如,舉個例子,一個用jsonp竊取token繞過后端檢測實現CSRF的例子:https://www.cnblogs.com/blacksunny/p/9124578.html

 

綜上,CSRF有以下特點:

CSRF的攻擊一般來源於第三方域名;CSRF不能獲取cookie但是可以借助瀏覽器特性去使用;接口的所有參數都是可以預測的或者說已知的

 

根據我上文所說,那么這里存在一個問題:cookie也是受到同源策略限制的,兩個不同源的網站其中一個應該是不會讓另一個獲得自己的cookie的,那為什么CSRF攻擊仍然有效?

 答:

  1. 除了跨域 XHR 請求情況下,瀏覽器在發起請求的時候會把符合要求的 cookie 自動帶上 (涉及:域名,有效期,路徑,secure 屬性)
  2. 跨域 XHR 的請求的情況下,也可以攜帶 cookie
  3. 瀏覽器允許跨域提交表單

cookie是可以跨域的,且在某些跨域情況下,瀏覽器中有頁面或網站向某個域名發送請求時,其請求都會自動帶上該域名下的所有滿足條件的 cookie

 

總而言之,雖然說如果沒有同源策略CSRF會更猖狂,但是同源策略阻止不了CSRF,同源策略確實是會攔截某些(不是全部)請求后的HTTP回復(而不是禁止請求執行),有些請求是可以跨域且附帶cookie的,還有些情況下你啪的一下子請求成功發出去了,這次的攻擊就完成了,后端程序已經執行了,是否有回復已經不重要了

 

 

一個cookie常見屬性如下(domain---cookie對於哪個域是有效的、expires---cookie失效時間、httpOnly---設置cookie是否能通過 js 去訪問,為true則不可以通過js訪問cookie只能在服務端改,防XSS讀取cookie例如document.cookie、path---這個cookie影響到的路徑、samesite---用於防CSRF、secure---安全標志,指定為true后,只有在使用SSL鏈接即HTTPS的時候才能發送到服務器,如果是http鏈接則不會傳遞該信息、value---存儲在cookie中的字符串值):

 

 

 其中,和我這篇文章相關性最強的samesite留到下一小點CSRF防御細說

 

 

0x02 防御

1.Referer(referrer)

HTTP請求頭中包含Referer字段表示請求來源,告訴服務器我是從哪個鏈接來的,如果是從惡意網站發出的請求Referer字段不會是正常的請求來源

這部分的校驗是在后台代碼中編寫的

 

2.Origin

與Referer思想類似,但區別在於Origin只存在於同域的post請求(Referer存在於所有類型請求)或者某些跨域情形(比如我前文說的CORS跨域)中(如果瀏覽器不能獲得請求源,但是滿足這倆條件,也會攜帶Origin只不過值為null),且只包含是誰(哪個源)發出的請求(origin包括協議和域名還有端口;referer除此之外還有路徑和查詢參數),沒有太多的信息,隱私方面好些。。。

 

 

3.token

說來說去,CSRF還是因為服務器無法判斷當前的請求是否是合法用戶的自定義操作

所以抵御CSRF的關鍵在於在正常請求中加上一些攻擊者不能偽造的東西,且其不存在於cookie,靈魂發問如何證明你是你

於是可以在HTTP請求中單獨加入一個參數---一個隨機生成的令牌---token

服務器在用戶登錄后,會給用戶一個唯一的合法令牌,每次操作都會對此令牌進行校驗

對於post請求可以以類似下面的方式把token以參數的形式加入請求

 

<form method="post" action="/transfer">

  <input type="hidden" name="csrf_token" value="token_value"/>

  <input type="text" name="amount"/>    

  <input type="hidden" name="account"/>

       <input type="submit" value="Transfer"/>

</form>

 

這里有小伙汁有個問題:如果放在了頁面表單中的hidden字段中,那攻擊者是不是可以通過下載頁面的源碼找到隱藏字段並再次進行偽造請求呢?

很顯然是不可能的,因為每個用戶的token都不一樣,我的是我的,你的是你的,攻擊者下載頁面得到的token在被攻擊者那邊無效,根本通不過校驗,而且攻擊者是無法通過CSRF 拿到被攻擊者的token的

 

 

可以把token放進cookie中 ,但是不能僅僅把token放在cookie中(當然存放token的cookie不要設置HTTP only,否則自己都無法正常使用了,如果有XSS能造成危害那另說),它必須也作為一個參數存在於HTTP請求體中

我們需要以表單提交的方式把token提供給后台服務器,后台服務器可以比較作為參數提交到服務器的token和用戶cookie中的token是否一致,一致則繼續校驗token進行下一步操作,不一致直接GG

或者把token存在服務器session中,用戶進行操作時,服務器會從此用戶對應的session文件中取出token與用戶提交到服務器的token進行某種對比,如果比較成功,則正常執行功能,否則我有理由懷疑你是CSRF

 

正如前文所說,CSRF最多也就是通過瀏覽器自動帶上符合條件的cookie,卻無法操作cookie來獲取token並加進HTTP請求的參數中(這位師傅研究了一些泄露CSRF token的方法:https://xz.aliyun.com/t/7084),猜不到參數也無法偽造正確的token

這正是token能防CSRF的根本原因

 

 

4.samesite

 此屬性專門為了防止CSRF

有三個值:Strict、Lax、None

Strict:最嚴格,完全禁止第三方cookie ,跨站時任何情況下都不會發送cookie,只有同站請求允許攜帶cookie(例如在一個網站中有一個鏈接,點擊會跳轉到GitHub上,在設置了strict的情況下,GitHub永遠都是未登錄狀態)

Set-Cookie: CookieName=CookieValue; SameSite=Strict;

Lax:一般嚴格,大多數情況下不發送第三方cookie,導航到目標網址的GET請求除外,導航到目標網址的GET請求只包括三種情況:鏈接、預加載請求、GET表單,即除了同站請求允許攜帶cookie之外也有特定的情況允許攜帶cookie

Set-Cookie: CookieName=CookieValue; SameSite=Lax;

 

附帶Strict與Lax之后,基本可以杜絕CSRF

 None:關閉samesite屬性(前提是必須同時設置Secure屬性,否則無效),同站跨站請求都可以攜帶cookie

一個有效的設置:

Set-Cookie: widget_session=abc123; SameSite=None; Secure

 參考鏈接:http://www.ruanyifeng.com/blog/2019/09/cookie-samesite.html

詳細參考:https://zhuanlan.zhihu.com/p/137408482

 

 

 5.驗證碼

思路與token類似,發送請求之前需要用戶輸入基於服務端判斷的某種驗證碼

這東西強制了用戶搞一個人機智障交互后,判斷成功了,才能實現正常的請求,很粗暴很有效

無力吐槽

這東西說到底是個輔助的二次驗證防御手段,如果每次每種操作都有驗證碼,體驗極差,用戶直接原地爆炸,不然就是開發者要爆炸

 

還有一些防君子不防小人的東西,思路類似的、或者殺敵一千自損八百的方法就不說了

在提交表單或者GET請求的地方,非靜態操作處,凡是沒有用戶進行二次驗證或沒有token的地方大概率存在csrf漏洞

 

0x03 繞過

可以利用點擊劫持(參考:https://www.cnblogs.com/-qing-/p/10871625.html)欺騙被攻擊者(請求確實是你自己點擊自己發出的啊)

或者更改請求方法(POST 改 GET)

如果采用了token驗證,且后台代碼存在問題,可以嘗試刪除token參數  或發送空token  或替換token

還有借助session固定攻擊(參考:https://blog.csdn.net/h_mxc/article/details/50542038)和我上文提到的雙token(請求參數與cookie中均有token)校驗鑽空子

如果有Referer驗證,可以嘗試去除Referer字段  或者后台基於白名單檢查Referer,可以嘗試繞過驗證URL的正則表達式,可以參考:https://cloud.tencent.com/developer/news/360329

 

參考:https://xz.aliyun.com/t/6176

 

 

 

未經允許,禁止轉載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM