跨站請求偽造

来源:http://www.cnblogs.com/4littleProgrammer/archive/2016/01/04/5089958.html
-Advertisement-
Play Games

1. 什麼是跨站請求偽造(CSRF) CSRF(Cross-site request forgery跨站請求偽造,也被稱為“One Click Attack”或者Session Riding,通常縮寫為CSRF或者XSRF,是一種對網站的惡意利用。儘管聽起來像跨站腳本(XSS),但它與XSS非常不同...


1. 什麼是跨站請求偽造(CSRF)

  CSRF(Cross-site request forgery跨站請求偽造,也被稱為“One Click Attack”或者Session Riding,通常縮寫為CSRF或者XSRF,是一種對網站的惡意利用。儘管聽起來像跨站腳本(XSS),但它與XSS非常不同,並且攻擊方式幾乎相左。XSS利用站點內的信任用戶,而CSRF則通過偽裝來自受信任用戶的請求來利用受信任的網站。與XSS攻擊相比,CSRF攻擊往往不大流行(因此對其進行防範的資源也相當稀少)和難以防範,所以被認為比XSS更具危險性。

  以上是來自百度百科的概念。下麵各自舉個簡單的例子說明:

  XSS:假設沒有預防XSS,我在文章評論區域輸入:<script>while(1)alert("呵呵")</script> 並且成功提交。那麼下次進來這個頁面進來,就會不斷彈出對話框,導致該頁面無法被正常瀏覽;對此我們在輸出內容時,如果該內容是用戶輸入的,那麼就應該進行Html Encode,如上面的腳本就會以普通文本的形式顯示出來。不僅如此,攻擊腳本還可以讀取用戶cookie信息,做任何腳本能做的事情。

  CSRF:與XSS不同,CSRF利用的當前信任用戶,讓用戶不知不覺的“自己提交”數據。例如用戶 B 在用戶 A 的文章評論區域上傳一張圖片,如:

[img]http://images2015.cnblogs.com/blog/798800/201512/798800-20151230205214839-1087717627.jpg[/img]

然後把它改成:

[img]http://Another/Forgery.html[/img]

這是一張無效的圖片,並且來自於另一個站點。在 Forgery.html 中,偽造者創建一個表單,action指向要攻擊的頁面,當window.onload 執行時自動提交表單。

那麼用戶 A 在訪問該頁面時,就會發起請求到Another/Forgery.html。由於Web的身份驗證信息通常會保存在瀏覽器cookie中,而cookie每次都會隨請求提交到伺服器,所以這個請求會把cookie 和 Forgery.html 的表單信息一起提交到伺服器。伺服器對Cookie進行相關驗證,並且認為這是一個正常的請求,執行相關操作。

2. 模擬一次攻擊

    按照上面的思路,接下來我們模擬一次攻擊。新建一個mvc項目,主要有兩個頁面,一個頁面用於顯示用戶姓名和評論,另一個頁面用於用戶自己修改姓名。只是為了演示,這裡我們固定一個用戶:張三。如:  

    public class CurrentUser
    {
        private static CurrentUser currentUser = new CurrentUser(){Name="張三"};
        public string Name{get;set;}
        public static CurrentUser Current
        {
            get
            {
                return currentUser;
            }
        }
    }

  顯示頁面為:

  

   本站點的另一個頁面用於修改用戶姓名:  

    <div>
        修改用戶信息:
    </div>
    <form action="/home/update" method="post">
        <p>
            <label>用戶名:</label>
            <input name="name" />
        </p>
        <p>
            <input type="submit" />
        </p>
    </form>

  

  對應Action為Update,為了提高安全性,我們給它標記一個[HttpPost]特性(實際情況這裡會進行身份驗證,然後根據用戶id去修改信息)。如:

        [HttpPost]
        public ActionResult Update(string name)
        {
            CurrentUser.Current.Name = name;
            return RedirectToAction("Index");
        }

  可以看到上面的評論區域有一個鏈接,來自另一個站點,它的代碼很簡單,與我們的修改頁面類似,如下:

<body>
    <div>
        <form id="form" action="http://localhost:50025/home/update" method="post">
            <input type="hidden" name="name" value="2b" />
        </form>
    </div>
</body>
<script type="text/javascript">
    window.onload = function () {
        document.getElementById("form").submit();
    }
</script>

  可以看到,該頁面表達的action指向了前面的站點的修改頁面,並且在頁面load完後,就會自動提交。當張三點擊這個鏈接後,會發生什麼呢?如下:

 

3. 如何防止

    3.1 儘早防範

  永遠不要相信用戶提交的數據。通常我們會在前臺和後臺對用戶的輸入進行驗證,確保數據的正確性和安全性。以上面的例子,如果用戶上傳一張圖片,然後修改成.html的格式,那麼應該不讓它保存。我們可以看博客園的例子,如果這樣做,可以保存,但顯示出來就是普通文本的格式,這樣頁面載入時就不會對這個url發起請求。再看csdn,則會彈出提示非法輸入。

  cnblogs:  

  csdn:   

  當然,這樣只是第一道屏障。很多網站也可能像上面可以輸入外部鏈接,如果用戶去點擊,依然可能被攻擊。所以我們還需要進一步防範。

    3.2 MVC 的做法

  前面我們模擬了CSRF的過程,接下看MVC里如何應對這種情況。

  ValidateAntiForgeryAttribute特性

  這是一個繼承了FilterAttribute 和 實現了 IActionFilter 的標記特性,它可以應用在Controller或者Action上面。我們知道實現IActionFilter的 Filter會在 Action執行前進行相關處理,具體邏輯在 IActionFilter介面的 OnAuthorization 方法中。MVC 就是在這個方法中進行驗證的。具體是如何驗證的呢?

  AntiForgeryToken方法

  ValidateAntiForgery特性表示操作需要驗證,我們還需要使用HtmlHelper的 AntiForgeryToken方法,這是一個實例方法。具體是在View的表單里調用該方法,該方法會生成一個name為__RequestVerificationToken的 input hidden標簽,值就是防偽令牌。除此之外,還會生成一個同樣名稱並且標記為HttpOnly的cookie,值也是通過加密生成的防偽令牌。ValidateAntiForgery特性的OnAuthorization方法就是根據這兩個進行驗證的。具體是:

  1. 用戶請求該頁面,AntiForgeryToken方法會生成一個input hidden 和 cookie,值都是經過加密處理的Token。

  2. 用戶提交請求,如果Action(Controller)標記了 ValidateAntiForgery特性,則進行驗證。

      2.1 如果表單沒有一個name為 __RequestVerificationToken的元素,則拋出HttpAntiForgeryException。

    2.2 如果沒有一個name為__RequestVerificationToken的cookie,則拋出HttpAntiForgeryException。

    2.3 解析input 和 cookie 的值,判斷是否匹配(包括用戶名、時間等的比較),不匹配則拋出HttpAntiForgeryException。

  3. 接收到異常,顯示錯誤頁或拋出黃頁。

  至於 input 的值 和 cookie 的生成,mvc內部會根據當前用戶名,時間以及集合 MachineKey 等去加密生成,確保不會輕易被猜出。有興趣的朋友可以通過源碼瞭解詳細過程。

  按照上面的做法,我們給Update加上一個[ValidateAntiForgery]特性,並且在表單調用HtmlHelper的AntiForgeryToken方法。此時如果用戶點擊鏈接,一樣訪問了Forgery.html,並且自動提交表單,cookie還是一樣會提交,但偽造頁面無法知道input hidden 的值,所以無法通過驗證。

    3.3 WebForm 的做法

  WebForm 沒有 AntiForgeryToken方法 可以直接使用,不過知道MVC的實現過程後,我們也可以自己實現一套。

  在頁面表單,像mvc一樣,我們也輸出一個名稱為:_RequestVerificationToken 的 input hidden 標簽,值為序列化後的Token,具體是調用 HttpRespose 的擴展方法AntiForgeryToken。AntiForgeryToken方法不僅會輸入input hidden,還會將Guid存儲在Context.Item,這是一個在一次請求內各個時期可以使用的集合,在頁面周期完成後,我們判斷是否有這個標記,如果有,還需要將它寫入到Cookie當中。

  表單:

    <form id="Form1" action="UpdateAntiCsrf.aspx" method="post" runat="server">
        <div>
            <%=Response.AntiForgeryToken() %>
            <input type="text" name="name"/>
            <input type="submit"/>
        </div>
    </form>

  AntiForgeryToken擴展方法:

    public static class HttpResposeExtentions
    {
        public static string AntiForgeryToken(this HttpResponse response)
        {
            HttpContext context = HttpContext.Current;
            if (context == null)
            {
                throw new InvalidOperationException("無效請求!");
            }
            Guid guid = Guid.NewGuid();
            context.Items["_RequestVerificationToken"] = guid;
            ObjectStateFormatter formatter = new ObjectStateFormatter();
            return string.Format("<input type='hidden' name='_RequestVerificationToken' value={0} />", formatter.Serialize(guid));                       
        }
    }

  對於驗證Token,和將GUID寫入到Cookie是通過一個AntiCsrfModule完成的,它主要攔截頁面執行前和執行後兩個事件。頁面執行後完成上面是否需要將GUID寫入Cookie的判斷,而頁面執行前則判斷是否需要驗證,以及驗證結果,一旦不匹配,就拋出異常。代碼如下:

    public class AntiCsrfModule : IHttpModule
    {
        public void Dispose ()
        {
        }

        public void Init(HttpApplication app) 
        {
            app.PreRequestHandlerExecute += new EventHandler(app_PreRequestHandlerExecute);
            app.PostRequestHandlerExecute += new EventHandler(app_PostRequestHandlerExecute);
        }        

        void app_PreRequestHandlerExecute(object sender, EventArgs e)
        {
            HttpContext context = ((HttpApplication)sender).Context;
            HttpRequest request = context.Request;
            IHttpHandler handler = context.Handler;
            if (handler.GetType().IsDefined(typeof(ValidationAntiForgeryAttribute), true))
            {
                if (request.HttpMethod.Equals("POST", StringComparison.CurrentCultureIgnoreCase))
                {
                    HttpCookie cookie = request.Cookies["_RequestVerificationToken"];
                    if (cookie == null)
                    {
                        throw new InvalidOperationException("無效請求!");
                    }
                    string value = request.Form["_RequestVerificationToken"];
                    if (string.IsNullOrEmpty(value))
                    {
                        throw new InvalidOperationException("無效請求!");
                    }
                    ObjectStateFormatter formatter = new ObjectStateFormatter();
                    Guid? guid = formatter.Deserialize(value) as Guid?;
                    if(guid.HasValue && guid.Value.ToString() == cookie.Value)
                    {
                        return;
                    }
                    throw new InvalidOperationException("無效請求!");
                }
            }
        }

        void app_PostRequestHandlerExecute(object sender, EventArgs e)
        {
            HttpContext context = ((HttpApplication)sender).Context;
            Guid? guid = context.Items["_RequestVerificationToken"] as Guid?;
            if (guid.HasValue)
            {
                HttpCookie cookie = new HttpCookie("_RequestVerificationToken", guid.Value.ToString());
                cookie.HttpOnly = false;                
                context.Response.Cookies.Add(cookie);
            }
        }
    }  

  對於需要驗證的頁面,通過一個ValidateAntiForgeryAttribute特性標記,如下:

    public class ValidateAntiForgeryAttribute : Attribute
    {
    }

  同樣,我們像前面一樣模擬一次攻擊。結果如我們所想,會拋出黃頁。

  

    3.4 Ajax 方式

  上面我們都是通過Post 表單的形式提交數據,如果是以ajax提交的呢?我們可以在後臺判斷請求是否是Ajax請求,如果不是則不允許操作。因為js受同源策略限制,另一個域在沒有被授權的情況下,腳本是無法和本域進行通信的。也就是Another/Forgery.html可以以post的形式提交數據到我們後臺,但沒辦法以ajax的形式提交,也沒辦法調用我們頁面的方法或者訪問dom元素。

4. 博客園的實現

  例子就在身邊。我們看到博客園【設置基本資料】模塊,查看源碼就會發現這裡用用到了這個技術。

  表單:

  

  Cookie:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 文章目的對於跨平臺編譯,網上有很多教程和解釋,但零零碎碎總感覺不完整,所以想集中整理一下。但跨平臺編譯是一個很寬泛的問題,如果要全部說清楚,涉及到的問題會有很多,多番查證文獻也會拖慢進度,所以決定隱藏不必要的技術細節,從“不求甚解”的角度,解釋一下跨平臺遇到的各種問題。預計隱藏的部分1.舉例子。比如...
  • 1 static void Ckeditor()2 {3 string tags = @"1234";4 //正則表達式的引擎是貪婪,只要模式允許,它將匹配儘可能多的字元。5 //如何匹配滿足條件的最短字元? 通...
  • 經過前面一段時間的努力,終於把我所知道的關於solr的內容都總結完了。前面講到了solr的安裝配置,web管理後臺的使用,solr的查詢參數和查詢語法,還說到了solr的客戶端solrnet的基本用法和Query,Facet,高亮等實際開發中的常用方法。可以說solr的相關的基礎的內容,都已經講.....
  • 封裝、繼承、多態並不是針對C#語言提出來的,他是一個在面向對象思想下產生的一個概念。所以想要弄明白封裝、繼承、多態,首先就要先瞭解面向對象概念。封裝:當我們提及面向對象的時候,這個對象怎麼來?就是通過我們人為的封裝得來。封裝就是把一些特征或功能組合到一個抽象的對象上。就比如說電腦:它的特征是有一個顯...
  • 現在有一個webform 網站 winform辦公系統,本來是相互獨立的,用的都是三層架構,dal,bll,ibll,model等等都一樣,現在想把wenform項目嵌套到winform中,就是共用dal,bll,ibll,model這些層,然而現在報錯,各種錯,例如:錯誤 157 未能找到元數據....
  • 目錄 Controller向View傳遞數據使用ViewData傳值數據使用ViewBag傳遞數據使用TampData傳遞數據使用Model傳遞數據 View向Controller傳遞數據通過Request.Form讀取表單數據通過FormCollection讀取表單數據模型綁定一、Controll...
  • 資料庫表之間有一對一 一對多 多對多關係。那同樣,CodeFirst也要能分析這些類之間的這些關係。CodeFirst可以自動通過分析類之間的屬性導航屬性 從而得出類之間的關係,自動確定外鍵。一對多一對多是最為常見的一種關係,符合怎樣的規範會被CodeFirst識別為一對多的關係呢? publ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...