jQuery的出現讓AJAX網頁的開發瞬間變簡單了。只要寫支簡單的ASPX,用Request["..."]接入前端用jQuery.ajax()傳來的參數,馬上就實現了AJAX式的資料查詢、新增、修改、刪除功能。但是,小心不要寫出如下的程式碼:

protected void Page_Load(object sender, EventArgs e)
{
    if (Request["mode"] == "del")
    {
        try
        {
            CheckCookieForAuthentication();
            CheckPermission();
            Guid id = new Guid(Request["id"]);
            DataProcessor.DeleteData(id);
            Response.Write("OK");
        }
        catch (Exception ex)
        {
            //...Blah...
        }
        Response.End();
    }

看出問題在哪裡嗎? SQL Injection? new Guid(Request["id"])確保參數id必須是GUID,不致被塞入惡意程式碼。權限管控? CheckCookieForAuthentication()會檢查登入成功才有的Cookie作為身分識別、接著檢查使用者是否有權執行,看來夠嚴格。XSS? 除了id外沒有接受其他的輸入參數,因此沒有資料內容驗證的問題。那麼,梗呢? 梗到底在哪裡?

由這段程式的寫法來看,我們可以用webform.aspx?mode=del&id=[guid]的方式執行。換句話說,這支程式同時支援GET Request或POST Request呼叫,而這犯了兵家大忌!!!

永遠不要使用GET方式接收指令進行資料更新!

為什麼GET式的更新很危險? Browser不是都有XmlHttpRequest(XHR)跨網域限制嗎? User必須登入該網站,才能使用XHR對該ASPX進行存取,如果想要在第三方網頁加掛程式碼以XHR進行XSS攻擊,應該會因跨網域被擋下來,不是嗎?

講到重點了,XHR是有跨網域限制,但<script src="/img/loading.svg" data-src="...">沒有!

想像一個情境,假設有個芭樂小站部落格平台,用以上的GET寫法執行部落格文章的刪除。小白是芭樂小站站長,登入網站介面執行一些操作後,就跑去網路上打混閒晃。小黑是個駭客,想要刪除某篇看不爽的PO文,先查出該文的Blog GUID,然後寄封信給小白,在信件先嵌入幾張連到網路相簿的照片,例如: 殺很大那種,<img src="/img/loading.svg" data-src="httq://albumweb/someimage.jpg" />,最後多加個<img src="/img/loading.svg" data-src="httq://blah.com.tw/admin/delPost.aspx?mode=del&id=[guid]" style="display:none;" />。小白的瀏覽器剛做完部落格管理(或者在本機上保存了登入成功的Cookie)再收取WebMail,雖然WebMail多會警告該信內嵌外部資源,預設不下載及Show圖,但當然是敵不過想看圖的衝動滴。按下同意顯示圖片,由albumweb下載照片的同時,也會觸發delBlog.aspx?mode=del&id=[guid],此時瀏覽器會沿用先前登入成功的Cookie,以站長權限執行刪除作業。就這樣,圖也看了、文也刪了,還真的"殺很大"。

(是不是有些人現在才發現WebMail或Outlook外連圖片資安警告的用意? 它還被用來防止另一種更常見的機車伎倆,例如: 廣告信在圖片src加上你的email當成附加參數,開信看圖就等同宣告你的email是有效的,而且心腸很軟,有信都要稍微看一下以示禮貌,以後就會有收不完的廣告信了。XD )

GET式AJAX資料更新的最大問題在於可以用<img src="/img/loading.svg" data-src="...">之類的方法閃避XHR的跨網域限制。造成了可以從第三方的網頁盜用使用者Cookie、Session對原站發動攻擊的漏洞,因此在嚴謹的設計中,許多Web Service是完全禁止GET式存取的。例如: 在ASP.NET AJAX裡,所有WebMethod只接受HTTP POST verb,更進一步還要求檢核Content-Type: application/json標頭,做到雙重保險。

在以上的例子裡,我們可以加上(Request.HttpMethod == "POST")的檢查限阻止GET請求,若是用WCF等機制預設都有相關保護,不要隨意亂改設定即可。但這些都只算基本防護,如果要談更進一步的管控,可以在登入完成後,在網頁上產生一個Token(不要放入Cookie,降低被盜風險),在執行與這個網頁相關的資料查詢、更新動作時,一併傳回Token驗明正身,這樣便可以將更嚴格限定只能由該網頁發出相關需求。

在網路世界裡,處處埋藏殺機,開發者不可不慎。


Comments

# by weskerjax

正所謂 CSRF 漏洞缺失 OWASP 關注的十大缺失之一 http://www.owasp.org/index.php/Taiwan#.E5.8D.81.E5.A4.A7Web.E8.B3.87.E5.AE.89.E6.BC.8F.E6.B4.9E.E5.88.97.E8.A1.A8

# by Tom

寫筆記中... 有學到...

# by andy

請問(在網頁上產生一個Token) 有範例可參考嗎?

# by Ark

我的做法是比對Request.Url & Request.UrlReferrer 來判別

# by 布丁

同事的做法是request method為get時,sql帳號使用Select only權限,request method為post時,則用可修改資料權限的帳號。

# by 海角147號

最保險的方法還是最好把每個傳入ASPX的參數做檢查 包含 POST 和 GET 都要檢查..!! 只要其中一個參數不和規則,就自動 Throw new Exception 檔掉 另外,傳入資料庫的 SQL Command 最好是先用字串型態接收參數,然後再 CAST 或是 Convert過 例如: declare @param1 nvarchar(50) , @param2 nvarchar(50) Select @param1 = N'%param1%' , @param2 = N'%param2%' Select * From [TABLE1] Where [column1] = CAST(@param As int) And [column2] = CAST(@param As datetime)

# by jocosn

「那麼,梗呢? 梗到底在哪裡?」→ 這句好有趣,很好笑。黑大你好幽默耶!

# by 小黑

黑大你是我的偶像,甚麼時候我才可以寫出不裸奔的網站呢?

# by 0.0

0.0

Post a comment


71 - 36 =