最近我跟一個漏洞還有一羣阿三幹起來了……javascript
背景:php
個人客戶是一個世界知名的藥企,最近這個客戶上臺了一位阿三管理者,這個貨上線第一個事兒就是要把現有的軟件供應商從新洗牌一遍。因爲咱們的客戶關係維護的很是好,直接對口人提早透露給咱們這個管理者就是想讓一個阿三公司壟斷他們的軟件供應,而且表示了很是鄙視。咱們表示了理解,畢竟任意一家公司只要進去一個阿三,慢慢的。。。慢慢的。。。就變成滿屋都是阿三。。。html
而後某一家阿三公司就暗地裏中標了,而後咱們就面臨KT。因爲咱們維護着12個高活躍系統,因此KT的工做量也是很是的大。java
BUT! 阿三的牛逼之處就在這時候體現出來了,他會從各個維度找你的事兒,其中一個就是找漏洞(本身找了一家阿三的漏洞檢測公司免費作)報給客戶並威脅說解決不完不接手,用以拉長KT的週期(原本KT只有三週時間)。web
而後客戶的阿三頭頭就贊成了。。。ajax
這個漏洞原本就有,客戶一直表示不想處理,由於大多數網站太老舊了,不少都不是咱們一手開發的。json
可是這回看來是不幹不行了,還好客戶表示會付費,行吧。。。 那就整後端
如今有請漏洞登場!跨域
你們好!我叫CSRF,全名是 Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet服務器
(Google Translate 瞭解一下)
這個玩意說白了就是一個假裝攻擊,假裝工具是Cookie。
這個玩意是這樣運做的:
(請不要在乎這個醜逼的圖。。。)
簡單描述就是
其餘網站用你的身份(Cookie)僞裝是你幹了你不知道的事兒,這時候請想一想你在網上銀行轉帳的時候
那麼這裏面就出現一個重大的疑點:
爲啥WebSiteB發過來的請求WebSiteA會收到呢? IIS吃了髒東西無論事兒了?
由於咱們的網站支持跨域請求!(是否是看着賊扎眼!畫重點了啊)
如今毛病基本OK了,剩的就是出方案。
對與CSRF這個東西知名度仍是很高的,網上一搜一大把
.NET MVC就自帶了解決方案,此方案只針對常規的MVC項目,先後端分離的繞行,之後我要是解決了我再回來寫。。。
解決方案也很粗暴,一句話來講就是:
咱們的服務器只接收來自咱們本身頁面發過來的請求
放到實現上就是:每一個頁面都按照必定規則生成一個Token,而後再發請求的時候帶過去,服務器先看Token再幹別的
這時候有人說了:要是別的網站僞造Token怎麼辦?
有道是孔子曰:不怕賊偷就怕賊惦記,他要是就想搞你,你遲早是防不住的啊,兄die
下面介紹關鍵代碼:
@Html.AntiForgeryToken()
這個是cshtml的頁面的代碼,aspx的差很少
這東西的做用是會在頁面上生成一個 Hidden,Value就是Token
最後變成Html長介樣兒:
<input name="__RequestVerificationToken" type="hidden" value="MbnNdB3T64quXYviXLsvoi_FlbM2SihwiiPCgSzaWAL0duMy7H6SbuF0lkUAxOD-DwF4P_4kxlyravohGXsQ_ERVPm5f3Oa3owG6LZ26WRw1" />
那一球亂糟糟的就是Token
那麼這玩意怎麼用呢?
Type 1,Form Request:
@using (Html.BeginForm("Action", "Controller", null, FormMethod.Post, new { id = "formId" })) { @Html.AntiForgeryToken(); Other Code...... }
Type 2,Ajax Request:
var token = $('@Html.AntiForgeryToken()').val(); var headers = {}; headers["__RequestVerificationToken"] = token; $.ajax({ type: "post", headers: headers, url: "@Url.Action("Action","Controller")", data: { }, dataType: "json", success: function (response) { } });
說到底就是頁面上生成了Token以後,想盡一切辦法發到後臺去,不拘泥與形式
form就是直接包到裏面了,後臺直接用name拿就ok了,Ajax是放在header裏了。
接下來就是後臺驗證,因爲絕大多數Action都須要堵這個漏洞,因此直接寫了一個Filter
using System.Net; using System.Web.Helpers; using System.Web.Mvc; public class ExtendedValidateAntiForgeryToken : AuthorizeAttribute { public override void OnAuthorization(AuthorizationContext filterContext) { var request = filterContext.HttpContext.Request; if (request.HttpMethod != WebRequestMethods.Http.Post) return; if (request.IsAjaxRequest()) { var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName]; var cookieValue = antiForgeryCookie != null ? antiForgeryCookie.Value : null; //從cookies 和 Headers 中 驗證防僞標記 //這裏能夠加try-catch //try //{ AntiForgery.Validate(cookieValue, request.Headers["__RequestVerificationToken"]); //} //catch (Exception e) //{ // //filterContext.Result = new RedirectResult("/Account/Login?returnUrl=" + // // HttpUtility.UrlEncode(filterContext.HttpContext.Request.Url.ToString())); // ContentResult result = new ContentResult(); // result.Content = "<div style='text-align:center;padding:1em;' >當前已經處於退出狀態,請從新登陸</div>"; // filterContext.Result = result; //} } else { //try //{ new ValidateAntiForgeryTokenAttribute().OnAuthorization(filterContext); //} //catch (Exception ex) //{ // // //} } } }
裏面代碼核心就是驗證Token的有效性,用的是官方API方法,可是要區別一個事兒,就是前文提到了我們Ajax和Form帶Token的方式不同,因此須要判斷是否是AJAX Request,走兩個分支。
而後就是把Filter掛到Action上就好了。
好了,漏洞堵上了,用時2天,客戶賊開心,正在準備去找阿三幹仗的時候出岔子了。
細心的老鐵可能發現了,上面的解決方案都是POST請求啊,GET呢?
這個就是個事兒了,從網上調查的時候得知,這個CSRF全是針對POST的,壓根就無論GET。
好比這個文章:
https://stackoverflow.com/questions/35473856/asp-net-mvc-csrf-on-a-get-request
阿三哪一個什麼漏洞檢測公司發回來一堆GET的URL。。。
在跟客戶說明原委以後,客戶炸了。。。 要幹阿三,而後就發了一系列言辭犀利的郵件,也CC了他們哪一個阿三頭頭
最後阿三們看有點失控,一個是咱們POST改的太快了(47處),第二個是,沒想到客戶的IT急眼了。。。
這時的阿三很尷尬,在郵件裏回:咱們有很豐富的修改漏洞的經驗
WTF?!!
還沒等咱們說話,客戶直接回了一句:好!我如今約一個會,大家說說GET請求是怎麼回事兒
行了。。。 我去幫客戶幹仗了。。。
想一想跟印度人、韓國人、澳大利亞人加上我一箇中國人開英語的會我就腦仁兒疼。。。。。。
另外附加一個鏈接:
https://weblogs.asp.net/dixin/anti-forgery-request-recipes-for-asp-net-mvc-and-ajax