Asp.net安全架構之3:CSRF(跨站點請求偽造)


原理

CSRF,Cross Site Request Forgery,即跨站點請求偽造。

這種攻擊是指,在用戶正常登錄系統以后,攻擊者誘使用戶訪問一些非法鏈接,以執行一些非法操作。比如:如果刪除用戶操作(如,yourdomain.com/deluser?id=123)沒有經過防范CSRF的處理,那么,假設用戶登錄系統后,攻擊者誘使用戶同時訪問了攻擊者的站點的一個鏈接(該鏈接正好為yourdomain.com/deluser?id=123),那么,系統就會在用戶不知情的情況下丟失一個用戶。

在這個例子中,跨站請求中的鏈接之所以被正常執行,首先是因為請求中瀏覽器正常發送了yourdomain的認證信息(一般保存在cookie中),服務器根本不知道該請求是用戶為之,還是惡意為之。其次,就是請求中的參數是可以被猜測的。這兩個條件,構成了CSRF攻擊的全部條件。

這里還需要強調一下,如果認證基於cookie,那么實際上還有第三個條件:如果cookie是本地cookie,瀏覽器還需要允許跨域發送本地cookie,即如果請求是第三方網站發起的,應帶上請求的域的cookie。IE默認是不允許跨域發送本地cookie的(session cookie則無此限制),而firefoxe就默認允許的。

原理如圖:

應對策略

1:采用token的形式。

采用token是指讓請求所帶的參數變的不可猜測。即,每次需要保護的請求都要帶上一個額外的參數,該參數可以是sessionid(一定要是額外的參數,但是其值可以為sessionid),也可以是另外的無法被猜測的一個值。然后,服務器在得到這個請求后,再驗證該值是否匹配。

可能有人會進一步提出,不是sessionid也是可以非法得到的嗎,或者說用戶的sessionid是沒有授權被操作的?答案是沒錯,但是,那又是另外的攻擊方法(涉及到會話劫持和權限欺騙)了,在這里,僅僅防御的是CSRF攻擊。

不過為了保險起見,我們可以用sessionid+salt,然后散列的方式來生成這個token。

采用token的形式,我們還需要考慮該token,也就是客戶端所帶的這個參數的保存問題。從CSRF的本質考慮,token的保存首先不能保存在cookie中,因為cookie本身就是在發送請求的時候可以被帶上。

其次,token可以保存在服務器端嗎,如,我們可以為當前請求設定一個唯一標識,然后保存在session中。答案當然也是不行的,我們可以假設完成一次請求包含兩個部分:發起請求的URL(或程序),處理請求的URL(或程序),誠然,這種方式我們防住了單獨請求”處理請求的URL”的CSRF攻擊。但是,既然攻擊者得到了處理請求的頁面,那么,他在偽造CSRF的時候,只要帶上了發送請求的頁面,就依然可以完成一次攻擊。

所以,token的保存只能是保存在發送到客戶端的頁面中,然后客戶端在接下來發送的請求的時候,帶上這個參數就可以了。當然,如果頁面本身已經被XSS攻破,那么攻擊者仍舊可以偽造一次合法請求,但這已經不是防范CSRF的范疇了,而是防范XSS。

2:每次需要被保護的請求發送時,都要求用戶輸入密碼;

3:每次需要被保護的請求發送時,都帶上referrer。不過這並不是應對的最佳策略,因為referrer是可以被輕易偽造的。

具體措施

以下具體措施針對token的形式。

n  遍歷前台所有發送請求的地方

1:文件查找前台所有的”svc”,”ajax”,”.aspx”,”.html”,”.htm”

2:文件查找前台所有的”form”

根據以上的查找,匯總到如下的表格:

序號

文件

代碼行

GET/POST

處理完成否

 

 

 

 

 

n  處理請求

篩選出需要進行CSRF處理的請求。然后對請求做如下處理:

如果是GET方式發送的請求,則為請求加入參數token=[value],其中[value]為sessionid的值;

如果是POST方式發送的請求,則為Form加入隱藏的input,其name為token,其值為sessionid。      

n  遍歷所有的請求處理處

1:遍歷所有的svc,為svc的方法增加token參數

2:遍歷所有的aspx頁面的code-behind

3:遍歷所有其它的后台方法,如果存在的話,如控制器方法(在EL中並不存在)。

根據以上的查找,匯總到如下的表格

序號

文件

代碼行

處理完成否

 

 

 

 

n  處理請求處理處

處理參數中的token,檢測該token是否存在於當前的sessionid中,如果存在,則放行,否則異常;

以上全部的邏輯用代碼表示,大致如下:

        protected void Page_Load(object sender, EventArgs e)
        {
            string token = CreateToken();
            PutTokenToClient(token);
            SaveTokenInServer(token);
        }

        protected void ButtonDosomething_Click(object sender, EventArgs e)
        {
            string token = GetTokenFromRequest();
            //需要csrf保護的地方就check才放行
            if (TokenIsOK(token))
            {
                //todo: go
            }
            else
            {
                //todo: block
            }
        }

        private string GetTokenFromRequest()
        {
            //todo 從請求中得到coken,一般為URL QueryString或表單元素
            throw new NotImplementedException();
        }

        private void PutTokenToClient(string token)
        {
            //todo 將其保存到前台,如請求的url,或隱藏的input
        }

        private void SaveTokenInServer(string token)
        {
            //一般保存在session中
            Session["CRSFToken"] = token;
        }

        private bool TokenIsOK(string token)
        {
            string tokenInServer = Session["CRSFToken"].ToString();
            return tokenInServer == token ? true : false;
        }

        public string _salt = "asdfkl@,.;#sss13131313";

        public string CreateToken()
        {
            return MD5(Session.SessionID + _salt);
        }

        private void ClearToken()
        {
            Session["CRSFToken"] = string.Empty;
        }

        private string MD5(string p)
        {
            throw new NotImplementedException();
    }

 


免責聲明!

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



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